import { useRef, useState } from "react"; import { Card, Dropdown, Radio } from "@elvis/ui"; import mandrillImage from "./assets/mandrill.png"; import marioImage from "./assets/mario.png"; import racoonImage from "./assets/racoon.png"; import cameramanImage from "./assets/cameraman.png"; import motocrossImage from "./assets/motocross.png"; import peppersImage from "./assets/peppers.png"; import usePoolingProcessing from "./usePoolingProcessing.ts"; import type { PoolingKernel } from "./usePoolingProcessing.ts"; const IMAGE_OPTIONS = ["Upload...", "Enter URL...", "Mandrill", "Cameraman", "Motocross", "Peppers", "Super Mario", "Racoon"] as const; const POOLING_KERNELS: PoolingKernel[] = [ "Max (per channel)", "Average (per channel)", "Max (across channels)", "Average (across channels)", ]; const MIN_KERNEL_SIZE = 1; const MAX_KERNEL_SIZE = 20; export default function PoolingVisualizer() { const [rawImage, setRawImage] = useState(mandrillImage); const [useColor, setUseColor] = useState(true); const [kernel, setKernel] = useState("Max (per channel)"); const [kernelWidth, setKernelWidth] = useState(2); const [kernelHeight, setKernelHeight] = useState(2); const [stride, setStride] = useState(2); const [dilation, setDilation] = useState(1); const [padding, setPadding] = useState(0); const result = usePoolingProcessing(rawImage, useColor, kernel, kernelWidth, kernelHeight, stride, dilation, padding); return (
setKernel(value as PoolingKernel)} />
updateNumber(event.currentTarget.valueAsNumber, setKernelWidth, 1)} /> × updateNumber(event.currentTarget.valueAsNumber, setKernelHeight, 1)} />
updateNumber(event.currentTarget.valueAsNumber, setStride, 1)} />
); } function updateNumber(value: number, setter: (value: number) => void, min: number) { if (!Number.isFinite(value)) return; setter(Math.round(Math.min(MAX_KERNEL_SIZE, Math.max(min, value)))); } function NumericOption({ label, value, min, onChange }: { label: string; value: number; min: number; onChange: (value: number) => void }) { return ( updateNumber(event.currentTarget.valueAsNumber, onChange, min)} /> ); } function ImagePane({ title, image, dimensions }: { title: string; image: string | null; dimensions: string | null }) { return (

{title}

{image ? {title} :

Processing...

}
{dimensions}
); } function ControlRow({ label, children }: { label: string; children: React.ReactNode }) { return (
{label}
{children}
); } function ImageControls({ setImage, useColor, setUseColor }: { setImage: (image: string) => void; useColor: boolean; setUseColor: (value: boolean) => void }) { const fileInput = useRef(null); const [selected, setSelected] = useState<(typeof IMAGE_OPTIONS)[number]>("Mandrill"); const [url, setUrl] = useState(""); const presets = { Mandrill: mandrillImage, Cameraman: cameramanImage, Motocross: motocrossImage, Peppers: peppersImage, "Super Mario": marioImage, Racoon: racoonImage }; function choose(option: (typeof IMAGE_OPTIONS)[number]) { if (option === "Upload...") return fileInput.current?.click(); setSelected(option); if (option !== "Enter URL...") setImage(presets[option]); } function applyUrl() { if (url.trim()) setImage(url.trim()); } return (
{ const file = event.target.files?.[0]; if (!file) return; const reader = new FileReader(); reader.onload = () => { if (typeof reader.result === "string") setImage(reader.result); }; reader.readAsDataURL(file); event.target.value = ""; }} /> {selected === "Enter URL..." && setUrl(e.target.value)} onBlur={applyUrl} onKeyDown={(e) => { if (e.key === "Enter") applyUrl(); }} />}
Color
setUseColor(option === "Color")} />
); }