Download src/hooks/useConvolution.ts from TechAvenger/kernel-convolution-visualizer: direct link, hf CLI and curl.
- Browser
- Download file 5.15 kB
-
https://huggingface.co/spaces/TechAvenger/kernel-convolution-visualizer/resolve/25e773913dbd4e9dcd6b40f787d68b280ccdfca1/src/hooks/useConvolution.ts
- Command line
-
hf download hf://spaces/TechAvenger/kernel-convolution-visualizer@25e773913dbd4e9dcd6b40f787d68b280ccdfca1/src/hooks/useConvolution.ts
-
curl -L -o useConvolution.ts https://huggingface.co/spaces/TechAvenger/kernel-convolution-visualizer/resolve/25e773913dbd4e9dcd6b40f787d68b280ccdfca1/src/hooks/useConvolution.ts
5.15 kB
| import { useCallback, useRef, useEffect } from 'react' | |
| import { useAppStore } from '@/store' | |
| import { convolutionSteps, convolveImage } from '@/lib/convolution/engine' | |
| import type { PaddingMode, ConvolutionStep } from '@/types' | |
| export function useConvolution() { | |
| const { | |
| originalImageData, | |
| selectedKernel, | |
| customKernelMatrix, | |
| customKernelNormalize, | |
| settings, | |
| convolution, | |
| startConvolution, | |
| pauseConvolution, | |
| resumeConvolution, | |
| stopConvolution, | |
| updateConvolutionStep, | |
| updateConvolutionProgress, | |
| completeConvolution, | |
| setConvolutionError, | |
| resetConvolution, | |
| } = useAppStore() | |
| const animationRef = useRef<number | null>(null) | |
| const generatorRef = useRef<Generator<ConvolutionStep> | null>(null) | |
| const isPausedRef = useRef(false) | |
| const stepModeRef = useRef(false) | |
| const kernel = selectedKernel?.matrix ?? customKernelMatrix | |
| const normalize = selectedKernel?.normalize ?? customKernelNormalize | |
| const padding = settings.padding | |
| const runConvolution = useCallback(async () => { | |
| if (!originalImageData || !kernel) return | |
| try { | |
| startConvolution() | |
| const extendedImage = { | |
| data: originalImageData.data, | |
| width: originalImageData.width, | |
| height: originalImageData.height, | |
| channels: 4, | |
| } | |
| const gen = convolutionSteps(extendedImage, kernel, padding, normalize, (progress) => { | |
| const totalPixels = originalImageData.width * originalImageData.height * 4 | |
| const processedPixels = Math.round(progress * totalPixels) | |
| const elapsedTime = Date.now() - convolution.startTime | |
| updateConvolutionProgress(progress, processedPixels, elapsedTime) | |
| }) | |
| generatorRef.current = gen | |
| const processBatch = () => { | |
| if (isPausedRef.current || stepModeRef.current) { | |
| animationRef.current = requestAnimationFrame(processBatch) | |
| return | |
| } | |
| const batchSize = Math.max(1, Math.floor(1000 * settings.animationSpeed)) | |
| let lastStep: ConvolutionStep | null = null | |
| for (let i = 0; i < batchSize; i++) { | |
| const result = gen.next() | |
| if (result.done) { | |
| if (lastStep && lastStep.x === -1) { | |
| // Final step with output image | |
| const outputImageData = new ImageData( | |
| new Uint8ClampedArray(originalImageData.data.length), | |
| originalImageData.width, | |
| originalImageData.height | |
| ) | |
| // We need to reconstruct from the generator's final output | |
| // For now, use the direct convolution | |
| const output = convolveImage(originalImageData, kernel, padding, normalize) | |
| completeConvolution(output) | |
| } | |
| return | |
| } | |
| lastStep = result.value | |
| updateConvolutionStep(result.value) | |
| } | |
| animationRef.current = requestAnimationFrame(processBatch) | |
| } | |
| animationRef.current = requestAnimationFrame(processBatch) | |
| } catch (error) { | |
| setConvolutionError(error instanceof Error ? error.message : 'Convolution failed') | |
| } | |
| }, [ | |
| originalImageData, | |
| kernel, | |
| normalize, | |
| padding, | |
| settings.animationSpeed, | |
| convolution.startTime, | |
| startConvolution, | |
| updateConvolutionStep, | |
| updateConvolutionProgress, | |
| completeConvolution, | |
| setConvolutionError, | |
| ]) | |
| const pause = useCallback(() => { | |
| isPausedRef.current = true | |
| pauseConvolution() | |
| }, [pauseConvolution]) | |
| const resume = useCallback(() => { | |
| isPausedRef.current = false | |
| resumeConvolution() | |
| }, [resumeConvolution]) | |
| const stop = useCallback(() => { | |
| if (animationRef.current) { | |
| cancelAnimationFrame(animationRef.current) | |
| animationRef.current = null | |
| } | |
| isPausedRef.current = false | |
| stepModeRef.current = false | |
| generatorRef.current = null | |
| stopConvolution() | |
| }, [stopConvolution]) | |
| const step = useCallback(() => { | |
| if (!generatorRef.current) return | |
| stepModeRef.current = true | |
| const result = generatorRef.current.next() | |
| if (!result.done) { | |
| updateConvolutionStep(result.value) | |
| } else if (result.value && result.value.x === -1) { | |
| const output = convolveImage(originalImageData!, kernel, padding, normalize) | |
| completeConvolution(output) | |
| } | |
| }, [originalImageData, kernel, padding, normalize, updateConvolutionStep, completeConvolution]) | |
| const reset = useCallback(() => { | |
| stop() | |
| resetConvolution() | |
| }, [stop, resetConvolution]) | |
| const toggleStepMode = useCallback((enabled: boolean) => { | |
| stepModeRef.current = enabled | |
| }, []) | |
| useEffect(() => { | |
| return () => { | |
| if (animationRef.current) { | |
| cancelAnimationFrame(animationRef.current) | |
| } | |
| } | |
| }, []) | |
| return { | |
| convolution, | |
| runConvolution, | |
| pause, | |
| resume, | |
| stop, | |
| step, | |
| reset, | |
| toggleStepMode, | |
| isRunning: convolution.status === 'running', | |
| isPaused: convolution.status === 'paused', | |
| isCompleted: convolution.status === 'completed', | |
| canRun: !!originalImageData && !!kernel && convolution.status !== 'running', | |
| } | |
| } |