diff --git a/TUICallKit-Vue3/components/dev-training-entry/index.vue b/TUICallKit-Vue3/components/dev-training-entry/index.vue index 200a4ff75..5e3b3166c 100644 --- a/TUICallKit-Vue3/components/dev-training-entry/index.vue +++ b/TUICallKit-Vue3/components/dev-training-entry/index.vue @@ -477,11 +477,6 @@ $brand-light: #34d399; line-height: 1; } -.menu-icon-text { - font-size: 32rpx; - line-height: 1; -} - /* 哑铃小 icon */ .dumbbell-shape { width: 40rpx; diff --git a/TUICallKit-Vue3/training/pages/components/coach-sprite.vue b/TUICallKit-Vue3/training/pages/components/coach-sprite.vue index 9b1a1b4cb..fc0ccb3f0 100644 --- a/TUICallKit-Vue3/training/pages/components/coach-sprite.vue +++ b/TUICallKit-Vue3/training/pages/components/coach-sprite.vue @@ -41,7 +41,7 @@ * mode: * - demo 按示范节奏循环(speed 调快慢,paused 时站立待机、轻微呼吸) * - mirror 跟随用户实时动作幅度 amount / side - * - gesture 挥手 / 欢呼 / 点赞 / 待机 + * - gesture 挥手 / 欢呼 / 点赞 * 示范模式下教练每做完一次发出 rep 事件(领练模式据此计数,报数和画面同步)。 * 动作中不插入欢呼 / 弹跳:和动作节奏对不上的突变,看起来就是“闪一下”。 * @@ -67,7 +67,7 @@ const props = withDefaults( { move: 'squat', mode: 'demo', - gesture: 'idle', + gesture: 'wave', amount: 0, side: 'L', speed: 1, diff --git a/TUICallKit-Vue3/training/pose/__tests__/counters.test.ts b/TUICallKit-Vue3/training/pose/__tests__/counters.test.ts index c2a88611f..86aad2280 100644 --- a/TUICallKit-Vue3/training/pose/__tests__/counters.test.ts +++ b/TUICallKit-Vue3/training/pose/__tests__/counters.test.ts @@ -5,7 +5,8 @@ import { test } from 'node:test' import assert from 'node:assert/strict' import { createCounter, type CoachEvent } from '../counters.ts' -import { MISTAKE_POSES, MOTIONS, sampleMotion, type MoveId } from '../motions.ts' +import { MISTAKE_POSES, MOTIONS, type MoveId } from '../motions.ts' +import { sampleMotion } from './sampleMotion.ts' import type { Pose } from '../keypoints.ts' const MOVES: MoveId[] = ['squat', 'march', 'jack', 'raise'] diff --git a/TUICallKit-Vue3/training/pose/__tests__/history.test.ts b/TUICallKit-Vue3/training/pose/__tests__/history.test.ts index ad064fba3..282bcf879 100644 --- a/TUICallKit-Vue3/training/pose/__tests__/history.test.ts +++ b/TUICallKit-Vue3/training/pose/__tests__/history.test.ts @@ -19,6 +19,7 @@ const summary = (reps: number): SessionSummary => ({ stars: 3, scores: [], advice: '', + adviceKind: 'praise', }) const DAY = 86400000 diff --git a/TUICallKit-Vue3/training/pose/__tests__/sampleMotion.ts b/TUICallKit-Vue3/training/pose/__tests__/sampleMotion.ts new file mode 100644 index 000000000..6c1d7dcb2 --- /dev/null +++ b/TUICallKit-Vue3/training/pose/__tests__/sampleMotion.ts @@ -0,0 +1,15 @@ +/** 单测辅助:把示范动作按时间采样成 Pose 序列(文件名不带 .test,不会被当成用例执行) */ +import type { Pose } from '../keypoints.ts' +import { MOTIONS, type MoveId } from '../motions.ts' + +export function sampleMotion(id: MoveId, seconds: number, fps = 12, startMs = 0): Pose[] { + const spec = MOTIONS[id] + const frames: Pose[] = [] + const n = Math.round(seconds * fps) + for (let i = 0; i < n; i++) { + const tSec = i / fps + const points = spec.poseAt(tSec / spec.cycleSeconds) + frames.push({ points, scores: points.map(() => 0.9), t: startMs + tSec * 1000 }) + } + return frames +} diff --git a/TUICallKit-Vue3/training/pose/__tests__/session.test.ts b/TUICallKit-Vue3/training/pose/__tests__/session.test.ts index 35ac2cc78..1bb5381d2 100644 --- a/TUICallKit-Vue3/training/pose/__tests__/session.test.ts +++ b/TUICallKit-Vue3/training/pose/__tests__/session.test.ts @@ -1,7 +1,8 @@ import { test } from 'node:test' import assert from 'node:assert/strict' import { TrainingSession, summarize, type SessionEvent } from '../session.ts' -import { MOTIONS, sampleMotion } from '../motions.ts' +import { MOTIONS } from '../motions.ts' +import { sampleMotion } from './sampleMotion.ts' import type { Pose } from '../keypoints.ts' const standing = (t: number): Pose => { diff --git a/TUICallKit-Vue3/training/pose/bgm.ts b/TUICallKit-Vue3/training/pose/bgm.ts index f722b2578..2d677d5d6 100644 --- a/TUICallKit-Vue3/training/pose/bgm.ts +++ b/TUICallKit-Vue3/training/pose/bgm.ts @@ -177,5 +177,3 @@ export function createBgm(opts: BgmOptions) { }, } } - -export type Bgm = ReturnType diff --git a/TUICallKit-Vue3/training/pose/coachFrames.ts b/TUICallKit-Vue3/training/pose/coachFrames.ts index 0f9146dec..5bcf5c45a 100644 --- a/TUICallKit-Vue3/training/pose/coachFrames.ts +++ b/TUICallKit-Vue3/training/pose/coachFrames.ts @@ -8,7 +8,7 @@ import type { MoveId, Side } from './motions.ts' import { COACH_HD, COACH_SEQUENCES, type CoachSequence } from './coachSequences.ts' -export type Gesture = 'idle' | 'wave' | 'cheer' | 'thumbs' +export type Gesture = 'wave' | 'cheer' | 'thumbs' const MOVE_SEQUENCE: Record> = { squat: { L: 'squat', R: 'squat' }, diff --git a/TUICallKit-Vue3/training/pose/keypoints.ts b/TUICallKit-Vue3/training/pose/keypoints.ts index df73c502d..35455a5b7 100644 --- a/TUICallKit-Vue3/training/pose/keypoints.ts +++ b/TUICallKit-Vue3/training/pose/keypoints.ts @@ -57,28 +57,6 @@ export const KP = { export const KEYPOINT_COUNT = 23 -/** 画骨架用的连线(左右成对,便于左右分色) */ -export const BONES: Array<[number, number]> = [ - [KP.L_SHOULDER, KP.R_SHOULDER], - [KP.L_HIP, KP.R_HIP], - [KP.L_SHOULDER, KP.L_HIP], - [KP.R_SHOULDER, KP.R_HIP], - [KP.L_SHOULDER, KP.L_ELBOW], - [KP.L_ELBOW, KP.L_WRIST], - [KP.L_WRIST, KP.L_HAND], - [KP.R_SHOULDER, KP.R_ELBOW], - [KP.R_ELBOW, KP.R_WRIST], - [KP.R_WRIST, KP.R_HAND], - [KP.L_HIP, KP.L_KNEE], - [KP.L_KNEE, KP.L_ANKLE], - [KP.L_ANKLE, KP.L_FOOT_OUT], - [KP.L_ANKLE, KP.L_FOOT_IN], - [KP.R_HIP, KP.R_KNEE], - [KP.R_KNEE, KP.R_ANKLE], - [KP.R_ANKLE, KP.R_FOOT_OUT], - [KP.R_ANKLE, KP.R_FOOT_IN], -] - /** 判定“全身入镜”需要看得到的点 */ export const FULL_BODY_POINTS = [ KP.NOSE, @@ -179,11 +157,6 @@ export function smoothPose(prev: Pose | null, next: Pose, alpha = 0.55): Pose { return { points, scores, t: next.t, aspect: next.aspect } } -/** 水平镜像(前置摄像头预览是镜像的) */ -export function mirrorPose(pose: Pose): Pose { - return { ...pose, points: pose.points.map((p) => ({ x: 1 - p.x, y: p.y })) } -} - type RawPoint = { x: number; y: number } | [number, number] /** diff --git a/TUICallKit-Vue3/training/pose/motions.ts b/TUICallKit-Vue3/training/pose/motions.ts index 7079bfc85..9c6f127db 100644 --- a/TUICallKit-Vue3/training/pose/motions.ts +++ b/TUICallKit-Vue3/training/pose/motions.ts @@ -8,7 +8,7 @@ * 坐标系与 VisionKit 一致:x/y 归一化到 [0,1],y 向下; * 被拍摄者的“左侧”在画面右侧(未镜像的正面视角)。 */ -import { KEYPOINT_COUNT, type Point, type Pose } from './keypoints.ts' +import { KEYPOINT_COUNT, type Point } from './keypoints.ts' export type MoveId = 'squat' | 'march' | 'jack' | 'raise' @@ -310,19 +310,6 @@ export const MISTAKE_POSES: Record Point[]> = { }, } -/** 把示范动作按时间采样成 Pose 序列(单测用) */ -export function sampleMotion(id: MoveId, seconds: number, fps = 12, startMs = 0): Pose[] { - const spec = MOTIONS[id] - const frames: Pose[] = [] - const n = Math.round(seconds * fps) - for (let i = 0; i < n; i++) { - const tSec = i / fps - const points = spec.poseAt(tSec / spec.cycleSeconds) - frames.push({ points, scores: points.map(() => 0.9), t: startMs + tSec * 1000 }) - } - return frames -} - /** * 示范动作“完成一次”的检测:幅度到过顶点、再回落到起始附近,算完成一次。 * 领练模式按教练动画计数,报数和画面同步(按时钟计数会比教练做完晚半拍)。 diff --git a/TUICallKit-Vue3/training/static/coach/idle.png b/TUICallKit-Vue3/training/static/coach/idle.png deleted file mode 100644 index b96165583..000000000 Binary files a/TUICallKit-Vue3/training/static/coach/idle.png and /dev/null differ diff --git a/training-videos/README.md b/training-videos/README.md index ae8e79988..a4e93202c 100644 --- a/training-videos/README.md +++ b/training-videos/README.md @@ -36,7 +36,7 @@ AI 跟练的素材流水线:教练“小甄”动作帧(AI 生成连续动 - Codex 只能出约 1.5MP 的图,人物 300~450px 高:切帧时先 4 倍超分再缩到主帧尺寸(站姿身高 720px) - 设定稿里手臂偶尔碰到隔壁或上一行的人物,切帧会按格子拆开;脚踝以下出现的肤色像素(别人的指尖)会被去掉 - 生成结果有随机性:先看 `work/preview/contact_*.jpg`,不满意就重新生成那一张;`coach_frames.py` 的 `MOVES` 可指定取哪些人物 -- 手势帧(idle / wave / cheer / thumbs)和错误示范帧(wrong_*)不随动作帧重新生成,导出时沿用 `public/coach/hd` 里已有的 +- 手势帧(wave / cheer / thumbs)和错误示范帧(wrong_*)不随动作帧重新生成,导出时沿用 `public/coach/hd` 里已有的 - 没有 `work/` 时,可用 `python3 scripts/coach_export.py --from-hd` 从高清帧重新导出小程序帧 ## 高清帧(COS) diff --git a/training-videos/public/coach/hd/idle.webp b/training-videos/public/coach/hd/idle.webp deleted file mode 100644 index 961371dcb..000000000 Binary files a/training-videos/public/coach/hd/idle.webp and /dev/null differ diff --git a/training-videos/scripts/coach_export.py b/training-videos/scripts/coach_export.py index 09466fa5c..e7b874494 100644 --- a/training-videos/scripts/coach_export.py +++ b/training-videos/scripts/coach_export.py @@ -32,8 +32,8 @@ CDN_MARGIN = 12 # 每个动作的帧名前缀:同一个动作的所有帧共用一个裁切框,帧与帧之间位置不变 MOVE_PREFIX = {"squat": ("sq_",), "raise": ("ra_",), "jack": ("jl_", "jr_"), "march": ("ml_", "mr_")} -STATIC_FRAMES = ["idle", "wave", "cheer", "thumbs", "wrong_squat", "wrong_march", "wrong_jack", "wrong_raise"] -MINI_STATIC = ["idle", "wave", "cheer", "thumbs"] +STATIC_FRAMES = ["wave", "cheer", "thumbs", "wrong_squat", "wrong_march", "wrong_jack", "wrong_raise"] +MINI_STATIC = ["wave", "cheer", "thumbs"] SEQUENCE_ORDER = ["squat", "raise", "jack_l", "jack_r", "march_l", "march_r"]