'use client'; import React, { useState, useEffect, useRef, useMemo } from 'react'; // 5x7 Pixel grids for digits 0-9 const DIGIT_PATTERNS = { 0: [ 0,1,1,1,0, 1,0,0,0,1, 1,0,0,0,1, 1,0,0,0,1, 1,0,0,0,1, 1,0,0,0,1, 0,1,1,1,0 ], 1: [ 0,0,1,0,0, 0,1,1,0,0, 0,0,1,0,0, 0,0,1,0,0, 0,0,1,0,0, 0,0,1,0,0, 0,1,1,1,0 ], 2: [ 0,1,1,1,0, 1,0,0,0,1, 0,0,0,0,1, 0,0,0,1,0, 0,0,1,0,0, 0,1,0,0,0, 1,1,1,1,1 ], 3: [ 0,1,1,1,0, 1,0,0,0,1, 0,0,0,0,1, 0,0,1,1,0, 0,0,0,0,1, 1,0,0,0,1, 0,1,1,1,0 ], 4: [ 0,0,0,1,0, 0,0,1,1,0, 0,1,0,1,0, 1,0,0,1,0, 1,1,1,1,1, 0,0,0,1,0, 0,0,0,1,0 ], 5: [ 1,1,1,1,1, 1,0,0,0,0, 1,1,1,1,0, 0,0,0,0,1, 0,0,0,0,1, 1,0,0,0,1, 0,1,1,1,0 ], 6: [ 0,1,1,1,0, 1,0,0,0,0, 1,0,0,0,0, 1,1,1,1,0, 1,0,0,0,1, 1,0,0,0,1, 0,1,1,1,0 ], 7: [ 1,1,1,1,1, 0,0,0,0,1, 0,0,0,1,0, 0,0,1,0,0, 0,1,0,0,0, 0,1,0,0,0, 0,1,0,0,0 ], 8: [ 0,1,1,1,0, 1,0,0,0,1, 1,0,0,0,1, 0,1,1,1,0, 1,0,0,0,1, 1,0,0,0,1, 0,1,1,1,0 ], 9: [ 0,1,1,1,0, 1,0,0,0,1, 1,0,0,0,1, 0,1,1,1,1, 0,0,0,0,1, 0,0,0,0,1, 0,1,1,1,0 ] }; const VIEW_WIDTH = 1000; const VIEW_HEIGHT = 600; export const NeuralNetworkRecognition = () => { const [currentDigit, setCurrentDigit] = useState(3); const [phase, setPhase] = useState('idle'); // idle, scan, propagate, result, clear const [probabilities, setProbabilities] = useState(new Array(10).fill(0.05)); const [activePixels, setActivePixels] = useState(new Array(35).fill(0)); // Random noise generation for background matrix effect const bgParticles = useMemo(() => { return Array.from({ length: 50 }, () => ({ x: Math.random() * VIEW_WIDTH, y: Math.random() * VIEW_HEIGHT, opacity: Math.random() * 0.3 + 0.1, speed: Math.random() * 2 + 0.5, size: Math.random() * 2 + 1 })); }, []); const reset = () => { setPhase('clear'); setTimeout(() => { const nextDigit = Math.floor(Math.random() * 10); setCurrentDigit(nextDigit); setActivePixels(new Array(35).fill(0)); setProbabilities(new Array(10).fill(0.02)); // Reset to low noise setPhase('scan'); }, 1000); }; useEffect(() => { // Animation Loop Logic let timeout; if (phase === 'scan') { // Reveal pixels one by one const pattern = DIGIT_PATTERNS[currentDigit]; let step = 0; const interval = setInterval(() => { if (step >= 35) { clearInterval(interval); setPhase('propagate'); } else { setActivePixels(prev => { const next = [...prev]; // Add some noise to make it look "handwritten" const noise = Math.random() > 0.9 ? 0.5 : 0; next[step] = pattern[step] ? 1 : noise; return next; }); step++; } }, 20); // Fast scan return () => clearInterval(interval); } else if (phase === 'propagate') { // Wait for signal to travel timeout = setTimeout(() => { setPhase('result'); }, 1500); } else if (phase === 'result') { // Animate probabilities const targetProbs = new Array(10).fill(0).map((_, i) => { if (i === currentDigit) return 0.85 + Math.random() * 0.14; // High confidence return Math.random() * 0.1; // Low noise }); setProbabilities(targetProbs); timeout = setTimeout(() => { reset(); }, 3000); // Show result for 3 seconds } else if (phase === 'idle') { reset(); } return () => clearTimeout(timeout); }, [phase, currentDigit]); // Visual Configuration const inputGridSize = 25; const inputGridGap = 5; const inputStartX = 100; const inputStartY = 200; const hiddenLayerX = 450; const hiddenNodes = 12; const hiddenNodeSpacing = 35; const hiddenStartY = (VIEW_HEIGHT - (hiddenNodes * hiddenNodeSpacing)) / 2; const outputLayerX = 800; const outputNodes = 10; const outputNodeSpacing = 40; const outputStartY = (VIEW_HEIGHT - (outputNodes * outputNodeSpacing)) / 2; // Calculate connections (subset for performance) const connections1 = useMemo(() => { const lines = []; // Only connect active pixels to some hidden nodes to save rendering for (let i = 0; i < 35; i+=3) { for (let j = 0; j < hiddenNodes; j+=2) { const x1 = inputStartX + (i % 5) * (inputGridSize + inputGridGap) + inputGridSize; const y1 = inputStartY + Math.floor(i / 5) * (inputGridSize + inputGridGap) + inputGridSize / 2; const x2 = hiddenLayerX; const y2 = hiddenStartY + j * hiddenNodeSpacing; lines.push({ x1, y1, x2, y2, key: `l1-${i}-${j}` }); } } return lines; }, []); const connections2 = useMemo(() => { const lines = []; for (let i = 0; i < hiddenNodes; i++) { for (let j = 0; j < outputNodes; j++) { const x1 = hiddenLayerX; const y1 = hiddenStartY + i * hiddenNodeSpacing; const x2 = outputLayerX; const y2 = outputStartY + j * outputNodeSpacing; lines.push({ x1, y1, x2, y2, key: `l2-${i}-${j}` }); } } return lines; }, []); return ( {/* Background Matrix/Particles */} {bgParticles.map((p, i) => ( ))} {/* Decorative Grid Lines */} NEURAL_NET_V1.0 // 图像识别模块 STATUS: {phase === 'scan' ? 'SCANNING...' : phase === 'propagate' ? 'PROCESSING...' : phase === 'result' ? 'IDENTIFIED' : 'IDLE'} {/* Group: Input Layer */} 输入层 Input (28x28) {activePixels.map((val, i) => { const x = inputStartX + (i % 5) * (inputGridSize + inputGridGap); const y = inputStartY + Math.floor(i / 5) * (inputGridSize + inputGridGap); const opacity = val; return ( 0.5 ? '#fff' : '#1e293b'} fillOpacity={val > 0.5 ? 0.9 : 0.5} rx="2" filter={val > 0.5 ? 'url(#glow)' : ''} className="transition-all duration-300" /> ); })} {/* Scanning Line Effect */} {phase === 'scan' && ( )} {/* Group: Connections 1 (Input -> Hidden) */} {connections1.map((line) => ( {phase === 'propagate' && ( )} ))} {/* Group: Hidden Layer */} 隐藏层 Hidden Layer {Array.from({ length: hiddenNodes }).map((_, i) => { const cy = hiddenStartY + i * hiddenNodeSpacing; const isActive = phase === 'propagate'; return ( {isActive && ( )} ); })} {/* Group: Connections 2 (Hidden -> Output) */} {connections2.map((line, i) => ( {phase === 'propagate' && ( )} ))} {/* Group: Output Layer */} 输出层 Output (0-9) {probabilities.map((prob, i) => { const y = outputStartY + i * outputNodeSpacing; const barWidth = prob * 150; const isTarget = i === currentDigit && phase === 'result'; const color = isTarget ? '#10b981' : '#64748b'; return ( {/* Label */} {i} {/* Background Bar */} {/* Active Bar */} {/* Percentage Text */} {(prob * 100).toFixed(1)}% {/* Highlight Circle */} ); })} {/* Result Display Overlay */} 识别结果: {currentDigit} CONFIDENCE: {(probabilities[currentDigit] * 100).toFixed(2)}% ); };