|
|
import React from 'react';
|
|
|
import { motion } from 'framer-motion';
|
|
|
import { Activity, Dumbbell, Gamepad2, Stethoscope, Play, CheckCircle2 } from 'lucide-react';
|
|
|
import ClickReveal from './ClickReveal';
|
|
|
import KeyTermBadge from './KeyTermBadge';
|
|
|
import { PoseSkeletonDemo } from './AnimatedDiagram';
|
|
|
|
|
|
export const chapter2Slides = [
|
|
|
|
|
|
{
|
|
|
content: (
|
|
|
<div className="space-y-8">
|
|
|
<motion.div
|
|
|
initial={{ scale: 0 }}
|
|
|
animate={{ scale: 1 }}
|
|
|
transition={{ type: "spring", delay: 0.2 }}
|
|
|
className="w-24 h-24 mx-auto rounded-3xl bg-gradient-to-br from-purple-500 to-pink-500 flex items-center justify-center shadow-2xl shadow-purple-500/30"
|
|
|
>
|
|
|
<Activity className="w-12 h-12 text-white" />
|
|
|
</motion.div>
|
|
|
|
|
|
<div className="text-center">
|
|
|
<h1 className="text-4xl md:text-5xl font-black text-white mb-4">
|
|
|
What is Pose Estimation?
|
|
|
</h1>
|
|
|
<p className="text-xl text-white/70">Chapter 2 • Slide 1 of 5</p>
|
|
|
</div>
|
|
|
|
|
|
<motion.div
|
|
|
initial={{ opacity: 0, y: 20 }}
|
|
|
animate={{ opacity: 1, y: 0 }}
|
|
|
transition={{ delay: 0.4 }}
|
|
|
className="bg-white/5 backdrop-blur-xl rounded-3xl p-8 border border-white/10"
|
|
|
>
|
|
|
<p className="text-xl text-white/90 leading-relaxed mb-6">
|
|
|
<KeyTermBadge term="Pose Estimation" definition="Technology that detects human body positions by identifying key body points" color="purple" /> is like teaching a computer to understand <span className="text-purple-400 font-semibold">body language</span>!
|
|
|
</p>
|
|
|
|
|
|
<p className="text-lg text-white/70 leading-relaxed">
|
|
|
It finds important points on your body — like your nose, shoulders, elbows, and knees — and connects them to create a "skeleton" that shows how you're moving.
|
|
|
</p>
|
|
|
</motion.div>
|
|
|
|
|
|
<ClickReveal title="🎮 Cool Connection!" color="purple">
|
|
|
<p>Video games like Just Dance use pose estimation to track your dance moves in real-time!</p>
|
|
|
</ClickReveal>
|
|
|
</div>
|
|
|
)
|
|
|
},
|
|
|
|
|
|
|
|
|
{
|
|
|
content: (
|
|
|
<div className="space-y-8">
|
|
|
<div className="text-center">
|
|
|
<h1 className="text-4xl md:text-5xl font-black text-white mb-4">
|
|
|
Keypoints & Skeleton
|
|
|
</h1>
|
|
|
<p className="text-xl text-white/70">Building a digital body map</p>
|
|
|
</div>
|
|
|
|
|
|
<div className="grid md:grid-cols-2 gap-6">
|
|
|
<motion.div
|
|
|
initial={{ opacity: 0, y: 20 }}
|
|
|
animate={{ opacity: 1, y: 0 }}
|
|
|
transition={{ delay: 0.3 }}
|
|
|
>
|
|
|
<PoseSkeletonDemo />
|
|
|
</motion.div>
|
|
|
|
|
|
<div className="space-y-4">
|
|
|
<motion.div
|
|
|
initial={{ opacity: 0, x: 20 }}
|
|
|
animate={{ opacity: 1, x: 0 }}
|
|
|
transition={{ delay: 0.4 }}
|
|
|
className="bg-white/5 backdrop-blur-xl rounded-2xl p-5 border border-white/10"
|
|
|
>
|
|
|
<h3 className="text-xl font-bold text-white mb-3 flex items-center gap-2">
|
|
|
<span className="w-3 h-3 rounded-full bg-pink-500" />
|
|
|
Keypoints
|
|
|
</h3>
|
|
|
<p className="text-white/80">
|
|
|
<KeyTermBadge term="Keypoints" definition="Specific body locations like joints that the AI tracks" color="pink" /> are the important spots on your body that the AI tracks — typically 17 points including your nose, eyes, shoulders, elbows, wrists, hips, knees, and ankles.
|
|
|
</p>
|
|
|
</motion.div>
|
|
|
|
|
|
<motion.div
|
|
|
initial={{ opacity: 0, x: 20 }}
|
|
|
animate={{ opacity: 1, x: 0 }}
|
|
|
transition={{ delay: 0.5 }}
|
|
|
className="bg-white/5 backdrop-blur-xl rounded-2xl p-5 border border-white/10"
|
|
|
>
|
|
|
<h3 className="text-xl font-bold text-white mb-3 flex items-center gap-2">
|
|
|
<div className="w-8 h-0.5 bg-gradient-to-r from-purple-500 to-pink-500" />
|
|
|
Skeleton Lines
|
|
|
</h3>
|
|
|
<p className="text-white/80">
|
|
|
Lines connect the keypoints to show your body structure — like a stick figure that moves exactly like you!
|
|
|
</p>
|
|
|
</motion.div>
|
|
|
|
|
|
<motion.div
|
|
|
initial={{ opacity: 0, x: 20 }}
|
|
|
animate={{ opacity: 1, x: 0 }}
|
|
|
transition={{ delay: 0.6 }}
|
|
|
className="bg-gradient-to-br from-purple-500/20 to-pink-500/20 rounded-2xl p-5 border border-purple-500/30"
|
|
|
>
|
|
|
<p className="text-white/90 font-medium">
|
|
|
💡 Each keypoint has X, Y coordinates (position) and sometimes a confidence score!
|
|
|
</p>
|
|
|
</motion.div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
)
|
|
|
},
|
|
|
|
|
|
|
|
|
{
|
|
|
content: (
|
|
|
<div className="space-y-8">
|
|
|
<div className="text-center">
|
|
|
<h1 className="text-4xl md:text-5xl font-black text-white mb-4">
|
|
|
Real-World Uses
|
|
|
</h1>
|
|
|
<p className="text-xl text-white/70">Pose estimation helps everywhere!</p>
|
|
|
</div>
|
|
|
|
|
|
<div className="grid gap-6">
|
|
|
{[
|
|
|
{
|
|
|
icon: Dumbbell,
|
|
|
title: "Sports & Fitness",
|
|
|
description: "Coaches analyze athlete movements to improve technique. Fitness apps check if you're doing exercises correctly!",
|
|
|
color: "from-purple-500 to-pink-500"
|
|
|
},
|
|
|
{
|
|
|
icon: Gamepad2,
|
|
|
title: "Gaming & VR",
|
|
|
description: "Motion-controlled games track your body to make you the controller. Dance games know exactly how you move!",
|
|
|
color: "from-blue-500 to-purple-500"
|
|
|
},
|
|
|
{
|
|
|
icon: Stethoscope,
|
|
|
title: "Healthcare",
|
|
|
description: "Physical therapists use it to track patient recovery. It can even help detect early signs of movement disorders.",
|
|
|
color: "from-emerald-500 to-teal-500"
|
|
|
}
|
|
|
].map((item, i) => (
|
|
|
<motion.div
|
|
|
key={i}
|
|
|
initial={{ opacity: 0, x: -30 }}
|
|
|
animate={{ opacity: 1, x: 0 }}
|
|
|
transition={{ delay: 0.3 + i * 0.15 }}
|
|
|
className="bg-white/5 backdrop-blur-xl rounded-2xl p-6 border border-white/10 flex items-start gap-4"
|
|
|
>
|
|
|
<div className={`w-14 h-14 rounded-xl bg-gradient-to-br ${item.color} flex items-center justify-center flex-shrink-0`}>
|
|
|
<item.icon className="w-7 h-7 text-white" />
|
|
|
</div>
|
|
|
<div>
|
|
|
<h3 className="text-xl font-bold text-white mb-2">{item.title}</h3>
|
|
|
<p className="text-white/70">{item.description}</p>
|
|
|
</div>
|
|
|
</motion.div>
|
|
|
))}
|
|
|
</div>
|
|
|
|
|
|
<ClickReveal title="🏃 Amazing Fact!" color="purple">
|
|
|
<p>Olympic coaches use pose estimation to analyze athletes frame-by-frame, finding improvements invisible to the human eye!</p>
|
|
|
</ClickReveal>
|
|
|
</div>
|
|
|
)
|
|
|
},
|
|
|
|
|
|
|
|
|
{
|
|
|
content: (
|
|
|
<div className="space-y-8">
|
|
|
<div className="text-center">
|
|
|
<h1 className="text-4xl md:text-5xl font-black text-white mb-4">
|
|
|
See It Move!
|
|
|
</h1>
|
|
|
<p className="text-xl text-white/70">Watch pose estimation in action</p>
|
|
|
</div>
|
|
|
|
|
|
<AnimatedStickFigure />
|
|
|
|
|
|
<div className="grid md:grid-cols-3 gap-4">
|
|
|
{[
|
|
|
{ label: "Real-time Tracking", icon: "⚡" },
|
|
|
{ label: "17 Keypoints", icon: "📍" },
|
|
|
{ label: "Smooth Motion", icon: "🌊" }
|
|
|
].map((item, i) => (
|
|
|
<motion.div
|
|
|
key={i}
|
|
|
initial={{ opacity: 0, y: 20 }}
|
|
|
animate={{ opacity: 1, y: 0 }}
|
|
|
transition={{ delay: 0.5 + i * 0.1 }}
|
|
|
className="bg-white/5 rounded-xl p-4 border border-white/10 text-center"
|
|
|
>
|
|
|
<span className="text-2xl">{item.icon}</span>
|
|
|
<p className="text-white/80 mt-2 font-medium">{item.label}</p>
|
|
|
</motion.div>
|
|
|
))}
|
|
|
</div>
|
|
|
</div>
|
|
|
)
|
|
|
},
|
|
|
|
|
|
|
|
|
{
|
|
|
content: (
|
|
|
<div className="space-y-10">
|
|
|
<div className="text-center mb-8">
|
|
|
<div className="text-7xl mb-6">🎊</div>
|
|
|
<h1 className="text-5xl md:text-6xl font-black text-white mb-6">
|
|
|
Chapter Complete!
|
|
|
</h1>
|
|
|
<p className="text-2xl text-white/70">You've mastered Pose Estimation</p>
|
|
|
</div>
|
|
|
|
|
|
<div className="bg-gradient-to-br from-purple-500/20 to-pink-500/20 rounded-3xl p-10 border border-purple-500/30">
|
|
|
<h3 className="text-2xl font-bold text-white mb-6 flex items-center gap-3">
|
|
|
<CheckCircle2 className="w-8 h-8 text-purple-400" />
|
|
|
Key Takeaways
|
|
|
</h3>
|
|
|
<div className="grid gap-4">
|
|
|
{[
|
|
|
{ icon: "🎯", text: "Pose estimation tracks body movements using keypoints" },
|
|
|
{ icon: "🦴", text: "17 keypoints create a full body skeleton" },
|
|
|
{ icon: "⚽", text: "Used in sports, gaming, healthcare, and more" },
|
|
|
{ icon: "⚡", text: "Works in real-time for interactive applications" }
|
|
|
].map((item, i) => (
|
|
|
<motion.div
|
|
|
key={i}
|
|
|
initial={{ opacity: 0, x: -20 }}
|
|
|
animate={{ opacity: 1, x: 0 }}
|
|
|
transition={{ delay: 0.3 + i * 0.1 }}
|
|
|
className="flex items-center gap-4 bg-white/5 rounded-2xl p-5"
|
|
|
>
|
|
|
<div className="text-4xl">{item.icon}</div>
|
|
|
<p className="text-white/90 text-lg flex-1">{item.text}</p>
|
|
|
</motion.div>
|
|
|
))}
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div className="grid md:grid-cols-3 gap-6">
|
|
|
<div className="bg-white/5 rounded-2xl p-6 text-center border border-white/10">
|
|
|
<div className="text-4xl mb-3">🎯</div>
|
|
|
<div className="text-3xl font-bold text-purple-400">5</div>
|
|
|
<div className="text-white/60">Slides Completed</div>
|
|
|
</div>
|
|
|
<div className="bg-white/5 rounded-2xl p-6 text-center border border-white/10">
|
|
|
<div className="text-4xl mb-3">📍</div>
|
|
|
<div className="text-3xl font-bold text-pink-400">17</div>
|
|
|
<div className="text-white/60">Body Keypoints</div>
|
|
|
</div>
|
|
|
<div className="bg-white/5 rounded-2xl p-6 text-center border border-white/10">
|
|
|
<div className="text-4xl mb-3">👥</div>
|
|
|
<div className="text-3xl font-bold text-violet-400">Multi</div>
|
|
|
<div className="text-white/60">Person Tracking</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
)
|
|
|
}
|
|
|
];
|
|
|
|
|
|
function AnimatedStickFigure() {
|
|
|
const [pose, setPose] = React.useState(0);
|
|
|
const poses = ['standing', 'waving'];
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
const interval = setInterval(() => {
|
|
|
setPose(p => (p + 1) % poses.length);
|
|
|
}, 2000);
|
|
|
return () => clearInterval(interval);
|
|
|
}, []);
|
|
|
|
|
|
const poseConfigs = {
|
|
|
standing: {
|
|
|
head: { x: 50, y: 15 },
|
|
|
neck: { x: 50, y: 22 },
|
|
|
lshoulder: { x: 38, y: 25 },
|
|
|
rshoulder: { x: 62, y: 25 },
|
|
|
lelbow: { x: 32, y: 38 },
|
|
|
relbow: { x: 68, y: 38 },
|
|
|
lwrist: { x: 30, y: 50 },
|
|
|
rwrist: { x: 70, y: 50 },
|
|
|
hip: { x: 50, y: 50 },
|
|
|
lhip: { x: 42, y: 52 },
|
|
|
rhip: { x: 58, y: 52 },
|
|
|
lknee: { x: 42, y: 70 },
|
|
|
rknee: { x: 58, y: 70 },
|
|
|
lankle: { x: 42, y: 88 },
|
|
|
rankle: { x: 58, y: 88 }
|
|
|
},
|
|
|
waving: {
|
|
|
head: { x: 50, y: 15 },
|
|
|
neck: { x: 50, y: 22 },
|
|
|
lshoulder: { x: 38, y: 25 },
|
|
|
rshoulder: { x: 62, y: 25 },
|
|
|
lelbow: { x: 32, y: 38 },
|
|
|
relbow: { x: 75, y: 15 },
|
|
|
lwrist: { x: 30, y: 50 },
|
|
|
rwrist: { x: 85, y: 8 },
|
|
|
hip: { x: 50, y: 50 },
|
|
|
lhip: { x: 42, y: 52 },
|
|
|
rhip: { x: 58, y: 52 },
|
|
|
lknee: { x: 42, y: 70 },
|
|
|
rknee: { x: 58, y: 70 },
|
|
|
lankle: { x: 42, y: 88 },
|
|
|
rankle: { x: 58, y: 88 }
|
|
|
}
|
|
|
};
|
|
|
|
|
|
const currentPose = poseConfigs[poses[pose]];
|
|
|
const bones = [
|
|
|
['head', 'neck'],
|
|
|
['neck', 'lshoulder'],
|
|
|
['neck', 'rshoulder'],
|
|
|
['lshoulder', 'lelbow'],
|
|
|
['rshoulder', 'relbow'],
|
|
|
['lelbow', 'lwrist'],
|
|
|
['relbow', 'rwrist'],
|
|
|
['neck', 'hip'],
|
|
|
['hip', 'lhip'],
|
|
|
['hip', 'rhip'],
|
|
|
['lhip', 'lknee'],
|
|
|
['rhip', 'rknee'],
|
|
|
['lknee', 'lankle'],
|
|
|
['rknee', 'rankle'],
|
|
|
];
|
|
|
|
|
|
return (
|
|
|
<div className="relative max-w-md mx-auto">
|
|
|
<div className="aspect-[3/4] bg-gradient-to-br from-purple-900/50 to-pink-900/50 rounded-3xl overflow-hidden border border-white/10">
|
|
|
<svg className="w-full h-full" viewBox="0 0 100 100">
|
|
|
{/* Bones */}
|
|
|
{bones.map(([from, to], i) => (
|
|
|
<motion.line
|
|
|
key={i}
|
|
|
x1={currentPose[from].x}
|
|
|
y1={currentPose[from].y}
|
|
|
x2={currentPose[to].x}
|
|
|
y2={currentPose[to].y}
|
|
|
stroke="url(#poseGradient)"
|
|
|
strokeWidth="3"
|
|
|
strokeLinecap="round"
|
|
|
initial={false}
|
|
|
animate={{
|
|
|
x1: currentPose[from].x,
|
|
|
y1: currentPose[from].y,
|
|
|
x2: currentPose[to].x,
|
|
|
y2: currentPose[to].y
|
|
|
}}
|
|
|
transition={{ type: "spring", stiffness: 100, damping: 15 }}
|
|
|
/>
|
|
|
))}
|
|
|
|
|
|
{/* Keypoints */}
|
|
|
{Object.entries(currentPose).map(([key, pos], i) => (
|
|
|
<motion.circle
|
|
|
key={key}
|
|
|
r="4"
|
|
|
fill="#EC4899"
|
|
|
initial={false}
|
|
|
animate={{ cx: pos.x, cy: pos.y }}
|
|
|
transition={{ type: "spring", stiffness: 100, damping: 15 }}
|
|
|
/>
|
|
|
))}
|
|
|
|
|
|
<defs>
|
|
|
<linearGradient id="poseGradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
|
<stop offset="0%" stopColor="#8B5CF6" />
|
|
|
<stop offset="100%" stopColor="#EC4899" />
|
|
|
</linearGradient>
|
|
|
</defs>
|
|
|
</svg>
|
|
|
</div>
|
|
|
|
|
|
{/* Pose Label */}
|
|
|
<motion.div
|
|
|
key={pose}
|
|
|
initial={{ opacity: 0, y: 10 }}
|
|
|
animate={{ opacity: 1, y: 0 }}
|
|
|
className="mt-4 text-center"
|
|
|
>
|
|
|
<span className="px-6 py-2 rounded-full bg-gradient-to-r from-purple-500 to-pink-500 text-white font-bold text-lg">
|
|
|
{poses[pose].charAt(0).toUpperCase() + poses[pose].slice(1)}
|
|
|
</span>
|
|
|
</motion.div>
|
|
|
</div>
|
|
|
);
|
|
|
} |