| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524 |
- "use client"
- import { useState } from "react"
- import { Button } from "@/components/ui/button"
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
- import { Badge } from "@/components/ui/badge"
- import { Progress } from "@/components/ui/progress"
- import { Slider } from "@/components/ui/slider"
- import { Switch } from "@/components/ui/switch"
- import { Label } from "@/components/ui/label"
- import {
- RotateCw,
- FlipVerticalIcon as Flip,
- Contrast,
- Sun,
- Crop,
- Palette,
- Info,
- Play,
- Pause,
- SkipForward,
- CheckCircle,
- AlertCircle,
- } from "lucide-react"
- interface PreprocessingSettings {
- resize: boolean
- targetSize: number
- normalize: boolean
- augmentation: boolean
- rotation: number
- brightness: number
- contrast: number
- flip: boolean
- crop: boolean
- }
- interface ProcessingStep {
- id: string
- name: string
- description: string
- icon: any
- status: "pending" | "processing" | "completed" | "error"
- progress: number
- }
- const defaultSampleImages = [
- { id: "1", name: "风景画1.jpg", url: "/mountain-lake-painting.png" },
- { id: "2", name: "抽象艺术.jpg", url: "/colorful-geometric-abstract.png" },
- { id: "3", name: "人物肖像.jpg", url: "/artistic-portrait.png" },
- { id: "4", name: "花卉静物.jpg", url: "/floral-still-life.png" },
- ]
- interface DataPreprocessingStepProps {
- onNext?: () => void
- selectedImages?: string[]
- }
- export default function DataPreprocessingStep({ onNext, selectedImages = [] }: DataPreprocessingStepProps) {
- const [settings, setSettings] = useState<PreprocessingSettings>({
- resize: true,
- targetSize: 512,
- normalize: true,
- augmentation: true,
- rotation: 15,
- brightness: 10,
- contrast: 10,
- flip: true,
- crop: false,
- })
- const [processingSteps, setProcessingSteps] = useState<ProcessingStep[]>([
- {
- id: "quality-check",
- name: "质量检查",
- description: "检查图像分辨率、格式和完整性",
- icon: AlertCircle,
- status: "pending",
- progress: 0,
- },
- {
- id: "resize",
- name: "尺寸标准化",
- description: "将所有图像调整为统一尺寸",
- icon: Crop,
- status: "pending",
- progress: 0,
- },
- {
- id: "normalize",
- name: "数值归一化",
- description: "将像素值标准化到0-1范围",
- icon: Palette,
- status: "pending",
- progress: 0,
- },
- {
- id: "augmentation",
- name: "数据增强",
- description: "通过旋转、翻转等方式增加数据多样性",
- icon: RotateCw,
- status: "pending",
- progress: 0,
- },
- ])
- const [isProcessing, setIsProcessing] = useState(false)
- const [currentProcessingStep, setCurrentProcessingStep] = useState(0)
- const sampleImages =
- selectedImages.length > 0
- ? selectedImages.slice(0, 4).map((url, index) => ({
- id: `selected-${index}`,
- name: `选中图像${index + 1}.jpg`,
- url: url,
- }))
- : defaultSampleImages
- const [selectedImage, setSelectedImage] = useState(sampleImages[0])
- const updateSetting = (key: keyof PreprocessingSettings, value: any) => {
- setSettings((prev) => ({ ...prev, [key]: value }))
- }
- const startProcessing = async () => {
- setIsProcessing(true)
- setCurrentProcessingStep(0)
- // Reset all steps
- setProcessingSteps((prev) =>
- prev.map((step) => ({
- ...step,
- status: "pending" as const,
- progress: 0,
- })),
- )
- // Simulate processing each step
- for (let i = 0; i < processingSteps.length; i++) {
- setCurrentProcessingStep(i)
- // Update current step to processing
- setProcessingSteps((prev) =>
- prev.map((step, index) => (index === i ? { ...step, status: "processing" as const } : step)),
- )
- // Simulate progress
- for (let progress = 0; progress <= 100; progress += 10) {
- await new Promise((resolve) => setTimeout(resolve, 100))
- setProcessingSteps((prev) => prev.map((step, index) => (index === i ? { ...step, progress } : step)))
- }
- // Mark as completed
- setProcessingSteps((prev) =>
- prev.map((step, index) => (index === i ? { ...step, status: "completed" as const, progress: 100 } : step)),
- )
- }
- setIsProcessing(false)
- }
- const getImageTransform = () => {
- let transform = ""
- if (settings.rotation > 0) {
- transform += `rotate(${settings.rotation}deg) `
- }
- if (settings.flip) {
- transform += "scaleX(-1) "
- }
- return transform
- }
- const getImageFilter = () => {
- let filter = ""
- if (settings.brightness !== 0) {
- filter += `brightness(${100 + settings.brightness}%) `
- }
- if (settings.contrast !== 0) {
- filter += `contrast(${100 + settings.contrast}%) `
- }
- return filter
- }
- const completedSteps = processingSteps.filter((step) => step.status === "completed").length
- const totalSteps = processingSteps.length
- const handleNext = () => {
- if (onNext && completedSteps >= totalSteps) {
- onNext()
- }
- }
- return (
- <div className="space-y-6">
- {/* Introduction */}
- <Card className="bg-secondary/5 border-secondary/20">
- <CardHeader>
- <div className="flex items-center gap-2">
- <Info className="w-5 h-5 text-secondary" />
- <CardTitle className="text-lg font-serif">什么是数据预处理?</CardTitle>
- </div>
- </CardHeader>
- <CardContent>
- <p className="text-muted-foreground leading-relaxed">
- 数据预处理是确保AI模型能够有效学习的关键步骤。就像厨师在烹饪前需要清洗和切配食材一样,
- 我们需要对图像数据进行标准化处理,包括调整尺寸、增强数据多样性、检查质量等, 以提高模型训练的效果和稳定性。
- </p>
- </CardContent>
- </Card>
- <div className="grid lg:grid-cols-2 gap-6">
- {/* Settings Panel */}
- <Card>
- <CardHeader>
- <CardTitle className="font-serif">预处理设置</CardTitle>
- <CardDescription>调整预处理参数,观察对图像的影响</CardDescription>
- </CardHeader>
- <CardContent className="space-y-6">
- {/* Image Resize */}
- <div className="space-y-3">
- <div className="flex items-center justify-between">
- <Label htmlFor="resize" className="font-medium">
- 尺寸标准化
- </Label>
- <Switch
- id="resize"
- checked={settings.resize}
- onCheckedChange={(checked) => updateSetting("resize", checked)}
- />
- </div>
- {settings.resize && (
- <div className="space-y-2">
- <Label className="text-sm text-muted-foreground">目标尺寸: {settings.targetSize}px</Label>
- <Slider
- value={[settings.targetSize]}
- onValueChange={([value]) => updateSetting("targetSize", value)}
- min={256}
- max={1024}
- step={64}
- className="w-full"
- />
- </div>
- )}
- </div>
- {/* Normalization */}
- <div className="flex items-center justify-between">
- <div>
- <Label htmlFor="normalize" className="font-medium">
- 数值归一化
- </Label>
- <p className="text-xs text-muted-foreground">将像素值标准化到0-1范围</p>
- </div>
- <Switch
- id="normalize"
- checked={settings.normalize}
- onCheckedChange={(checked) => updateSetting("normalize", checked)}
- />
- </div>
- {/* Data Augmentation */}
- <div className="space-y-3">
- <div className="flex items-center justify-between">
- <Label htmlFor="augmentation" className="font-medium">
- 数据增强
- </Label>
- <Switch
- id="augmentation"
- checked={settings.augmentation}
- onCheckedChange={(checked) => updateSetting("augmentation", checked)}
- />
- </div>
- {settings.augmentation && (
- <div className="space-y-4 pl-4 border-l-2 border-muted">
- {/* Rotation */}
- <div className="space-y-2">
- <Label className="text-sm flex items-center gap-2">
- <RotateCw className="w-4 h-4" />
- 旋转角度: ±{settings.rotation}°
- </Label>
- <Slider
- value={[settings.rotation]}
- onValueChange={([value]) => updateSetting("rotation", value)}
- min={0}
- max={45}
- step={5}
- className="w-full"
- />
- </div>
- {/* Brightness */}
- <div className="space-y-2">
- <Label className="text-sm flex items-center gap-2">
- <Sun className="w-4 h-4" />
- 亮度调整: ±{settings.brightness}%
- </Label>
- <Slider
- value={[settings.brightness]}
- onValueChange={([value]) => updateSetting("brightness", value)}
- min={0}
- max={30}
- step={5}
- className="w-full"
- />
- </div>
- {/* Contrast */}
- <div className="space-y-2">
- <Label className="text-sm flex items-center gap-2">
- <Contrast className="w-4 h-4" />
- 对比度调整: ±{settings.contrast}%
- </Label>
- <Slider
- value={[settings.contrast]}
- onValueChange={([value]) => updateSetting("contrast", value)}
- min={0}
- max={30}
- step={5}
- className="w-full"
- />
- </div>
- {/* Flip */}
- <div className="flex items-center justify-between">
- <Label htmlFor="flip" className="text-sm flex items-center gap-2">
- <Flip className="w-4 h-4" />
- 水平翻转
- </Label>
- <Switch
- id="flip"
- checked={settings.flip}
- onCheckedChange={(checked) => updateSetting("flip", checked)}
- />
- </div>
- </div>
- )}
- </div>
- </CardContent>
- </Card>
- {/* Preview Panel */}
- <Card>
- <CardHeader>
- <CardTitle className="font-serif">预处理预览</CardTitle>
- <CardDescription>
- {selectedImages.length > 0 ? "使用你选择的图像进行预处理预览" : "使用示例图像进行预处理预览"}
- </CardDescription>
- </CardHeader>
- <CardContent>
- <div className="space-y-4">
- {/* Image Selector */}
- <div className="flex gap-2 overflow-x-auto pb-2">
- {sampleImages.map((image) => (
- <button
- key={image.id}
- onClick={() => setSelectedImage(image)}
- className={`flex-shrink-0 w-16 h-16 rounded-lg overflow-hidden border-2 transition-colors ${
- selectedImage.id === image.id ? "border-primary" : "border-border"
- }`}
- >
- <img
- src={image.url || "/placeholder.svg"}
- alt={image.name}
- className="w-full h-full object-cover"
- />
- </button>
- ))}
- </div>
- {/* Before/After Comparison */}
- <div className="grid grid-cols-2 gap-4">
- <div>
- <Label className="text-sm font-medium mb-2 block">原始图像</Label>
- <div className="aspect-square bg-muted rounded-lg overflow-hidden">
- <img
- src={selectedImage.url || "/placeholder.svg"}
- alt="原始图像"
- className="w-full h-full object-cover"
- />
- </div>
- </div>
- <div>
- <Label className="text-sm font-medium mb-2 block">预处理后</Label>
- <div className="aspect-square bg-muted rounded-lg overflow-hidden">
- <img
- src={selectedImage.url || "/placeholder.svg"}
- alt="预处理后"
- className="w-full h-full object-cover transition-all duration-300"
- style={{
- transform: getImageTransform(),
- filter: getImageFilter(),
- width: settings.resize ? `${Math.min(100, (settings.targetSize / 512) * 100)}%` : "100%",
- height: settings.resize ? `${Math.min(100, (settings.targetSize / 512) * 100)}%` : "100%",
- }}
- />
- </div>
- </div>
- </div>
- {/* Processing Info */}
- <div className="bg-muted/50 rounded-lg p-4 space-y-2">
- <h4 className="font-medium text-sm">预处理信息</h4>
- <div className="text-xs text-muted-foreground space-y-1">
- {settings.resize && (
- <p>
- • 尺寸: {settings.targetSize}×{settings.targetSize}px
- </p>
- )}
- {settings.normalize && <p>• 像素值归一化: 0-1范围</p>}
- {settings.augmentation && (
- <>
- {settings.rotation > 0 && <p>• 随机旋转: ±{settings.rotation}°</p>}
- {settings.brightness > 0 && <p>• 亮度变化: ±{settings.brightness}%</p>}
- {settings.contrast > 0 && <p>• 对比度变化: ±{settings.contrast}%</p>}
- {settings.flip && <p>• 随机水平翻转</p>}
- </>
- )}
- </div>
- </div>
- </div>
- </CardContent>
- </Card>
- </div>
- {/* Processing Pipeline */}
- <Card>
- <CardHeader>
- <div className="flex items-center justify-between">
- <div>
- <CardTitle className="font-serif">预处理流水线</CardTitle>
- <CardDescription>模拟数据预处理的完整流程</CardDescription>
- </div>
- <Button onClick={startProcessing} disabled={isProcessing} className="font-semibold">
- {isProcessing ? (
- <>
- <Pause className="w-4 h-4 mr-2" />
- 处理中...
- </>
- ) : (
- <>
- <Play className="w-4 h-4 mr-2" />
- 开始预处理
- </>
- )}
- </Button>
- </div>
- </CardHeader>
- <CardContent>
- <div className="space-y-4">
- {/* Overall Progress */}
- <div className="space-y-2">
- <div className="flex justify-between text-sm">
- <span>总体进度</span>
- <span>
- {completedSteps}/{totalSteps} 步骤完成
- </span>
- </div>
- <Progress value={(completedSteps / totalSteps) * 100} className="h-2" />
- </div>
- {/* Processing Steps */}
- <div className="space-y-3">
- {processingSteps.map((step, index) => {
- const Icon = step.icon
- return (
- <div
- key={step.id}
- className={`flex items-center gap-4 p-4 rounded-lg border transition-all ${
- step.status === "processing"
- ? "border-primary bg-primary/5"
- : step.status === "completed"
- ? "border-secondary bg-secondary/5"
- : "border-border"
- }`}
- >
- <div
- className={`p-2 rounded-md ${
- step.status === "processing"
- ? "bg-primary text-primary-foreground"
- : step.status === "completed"
- ? "bg-secondary text-secondary-foreground"
- : "bg-muted text-muted-foreground"
- }`}
- >
- {step.status === "completed" ? <CheckCircle className="w-5 h-5" /> : <Icon className="w-5 h-5" />}
- </div>
- <div className="flex-1">
- <h4 className="font-medium">{step.name}</h4>
- <p className="text-sm text-muted-foreground">{step.description}</p>
- {step.status === "processing" && (
- <div className="mt-2">
- <Progress value={step.progress} className="h-1" />
- </div>
- )}
- </div>
- <Badge
- variant={
- step.status === "completed" ? "default" : step.status === "processing" ? "secondary" : "outline"
- }
- >
- {step.status === "completed" ? "已完成" : step.status === "processing" ? "处理中" : "等待中"}
- </Badge>
- </div>
- )
- })}
- </div>
- </div>
- </CardContent>
- </Card>
- {/* Next Step Button */}
- <div className="flex justify-end">
- <Button size="lg" disabled={completedSteps < totalSteps} className="font-semibold" onClick={handleNext}>
- 继续到模型训练
- <SkipForward className="w-4 h-4 ml-2" />
- </Button>
- </div>
- </div>
- )
- }
|