Spaces:
Running
Running
Download frontends/react/src/PoolingVisualizer.tsx from elvis-hf/cnn_visualizer: direct link, hf CLI and curl.
- Browser
- Download file 7.58 kB
-
https://huggingface.co/spaces/elvis-hf/cnn_visualizer/resolve/main/frontends/react/src/PoolingVisualizer.tsx
- Command line
-
hf download hf://spaces/elvis-hf/cnn_visualizer/frontends/react/src/PoolingVisualizer.tsx
-
curl -L -o PoolingVisualizer.tsx https://huggingface.co/spaces/elvis-hf/cnn_visualizer/resolve/main/frontends/react/src/PoolingVisualizer.tsx
7.58 kB
| 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<PoolingKernel>("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 ( | |
| <div className="grid grid-cols-[minmax(0,7fr)_minmax(0,3fr)] gap-8 h-full min-h-0"> | |
| <Card className="grid grid-cols-2 gap-4 h-full min-h-0"> | |
| <ImagePane title="Input" image={result.input} dimensions={result.inputSize} /> | |
| <ImagePane title="Output" image={result.output} dimensions={result.outputSize} /> | |
| </Card> | |
| <Card className="flex flex-col gap-6 h-full min-h-0 overflow-auto"> | |
| <ImageControls setImage={setRawImage} useColor={useColor} setUseColor={setUseColor} /> | |
| <div className="flex flex-col w-full min-h-0 gap-3"> | |
| <ControlRow label="Kernel"> | |
| <Dropdown label="" options={POOLING_KERNELS} activeOption={kernel} onChange={(value) => setKernel(value as PoolingKernel)} /> | |
| </ControlRow> | |
| <ControlRow label="Size"> | |
| <div className="flex items-center gap-2"> | |
| <input type="number" min={MIN_KERNEL_SIZE} max={MAX_KERNEL_SIZE} aria-label="Pooling kernel width" className="w-20 px-2 py-1 text-sm text-center rounded border border-gray-300" value={kernelWidth} onChange={(event) => updateNumber(event.currentTarget.valueAsNumber, setKernelWidth, 1)} /> | |
| <span className="text-sm">×</span> | |
| <input type="number" min={MIN_KERNEL_SIZE} max={MAX_KERNEL_SIZE} aria-label="Pooling kernel height" className="w-20 px-2 py-1 text-sm text-center rounded border border-gray-300" value={kernelHeight} onChange={(event) => updateNumber(event.currentTarget.valueAsNumber, setKernelHeight, 1)} /> | |
| </div> | |
| </ControlRow> | |
| <ControlRow label="Stride"> | |
| <input | |
| type="number" | |
| min={1} | |
| max={MAX_KERNEL_SIZE} | |
| aria-label="Pooling stride" | |
| className="w-20 px-2 py-1 text-sm text-center rounded border border-gray-300" | |
| value={stride} | |
| onChange={(event) => updateNumber(event.currentTarget.valueAsNumber, setStride, 1)} | |
| /> | |
| </ControlRow> | |
| <NumericOption label="Dilation" value={dilation} min={1} onChange={setDilation} /> | |
| <NumericOption label="Padding" value={padding} min={0} onChange={setPadding} /> | |
| </div> | |
| </Card> | |
| </div> | |
| ); | |
| } | |
| 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 ( | |
| <ControlRow label={label}> | |
| <input | |
| type="number" | |
| min={min} | |
| max={MAX_KERNEL_SIZE} | |
| aria-label={`Pooling ${label.toLowerCase()}`} | |
| className="w-20 px-2 py-1 text-sm text-center rounded border border-gray-300" | |
| value={value} | |
| onChange={(event) => updateNumber(event.currentTarget.valueAsNumber, onChange, min)} | |
| /> | |
| </ControlRow> | |
| ); | |
| } | |
| function ImagePane({ title, image, dimensions }: { title: string; image: string | null; dimensions: string | null }) { | |
| return ( | |
| <div className="flex flex-col items-center justify-center min-h-0"> | |
| <h2 className="text-lg font-bold mb-2">{title}</h2> | |
| {image ? <img src={image} alt={title} className="flex-1 w-full h-full min-w-0 min-h-0 object-contain" /> : <p>Processing...</p>} | |
| <div className="h-8 shrink-0 text-xs text-gray-700 pt-2">{dimensions}</div> | |
| </div> | |
| ); | |
| } | |
| function ControlRow({ label, children }: { label: string; children: React.ReactNode }) { | |
| return ( | |
| <div className="grid grid-cols-[4.5rem_minmax(0,1fr)] items-center gap-2"> | |
| <span className="text-sm font-bold text-gray-700 text-right">{label}</span> | |
| <div className="justify-self-start text-sm [&_label]:hidden [&_select]:px-2 [&_select]:py-1 [&_select]:text-xs">{children}</div> | |
| </div> | |
| ); | |
| } | |
| function ImageControls({ setImage, useColor, setUseColor }: { setImage: (image: string) => void; useColor: boolean; setUseColor: (value: boolean) => void }) { | |
| const fileInput = useRef<HTMLInputElement | null>(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 ( | |
| <div className="flex flex-col gap-3"> | |
| <input ref={fileInput} type="file" accept="image/*" className="hidden" onChange={(event) => { | |
| 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 = ""; | |
| }} /> | |
| <ControlRow label="Image"><Dropdown label="" options={IMAGE_OPTIONS} activeOption={selected} onChange={choose} /></ControlRow> | |
| {selected === "Enter URL..." && <ControlRow label=""><input type="url" value={url} placeholder="Enter image URL and press Enter" className="w-full rounded border border-gray-300 px-2 py-1 text-xs" onChange={(e) => setUrl(e.target.value)} onBlur={applyUrl} onKeyDown={(e) => { if (e.key === "Enter") applyUrl(); }} /></ControlRow>} | |
| <div className="grid grid-cols-[4.5rem_minmax(0,1fr)] items-center gap-2"> | |
| <span className="text-sm font-bold text-gray-700 text-right">Color</span> | |
| <div className="justify-self-start text-sm"> | |
| <Radio | |
| label="" | |
| options={["Grayscale", "Color"] as const} | |
| activeOption={useColor ? "Color" : "Grayscale"} | |
| onChange={(option) => setUseColor(option === "Color")} | |
| /> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| } | |