import { useEffect, useMemo, useState } from "react"; import "./App.css"; const API_URL = "/enhance"; const ACCEPTED_TYPES = ["image/png", "image/jpeg", "image/jpg", "image/webp"]; function App() { const [selectedFile, setSelectedFile] = useState(null); const [inputPreviewUrl, setInputPreviewUrl] = useState(""); const [outputPreviewUrl, setOutputPreviewUrl] = useState(""); const [isEnhancing, setIsEnhancing] = useState(false); const [errorMessage, setErrorMessage] = useState(""); const [isDragging, setIsDragging] = useState(false); const [successMessage, setSuccessMessage] = useState(""); const fileSizeInMb = useMemo(() => { if (!selectedFile) return ""; return `${(selectedFile.size / (1024 * 1024)).toFixed(2)} MB`; }, [selectedFile]); useEffect(() => { return () => { if (inputPreviewUrl) URL.revokeObjectURL(inputPreviewUrl); if (outputPreviewUrl) URL.revokeObjectURL(outputPreviewUrl); }; }, [inputPreviewUrl, outputPreviewUrl]); const clearResult = () => { if (outputPreviewUrl) { URL.revokeObjectURL(outputPreviewUrl); } setOutputPreviewUrl(""); }; const resetAll = () => { if (inputPreviewUrl) URL.revokeObjectURL(inputPreviewUrl); if (outputPreviewUrl) URL.revokeObjectURL(outputPreviewUrl); setSelectedFile(null); setInputPreviewUrl(""); setOutputPreviewUrl(""); setErrorMessage(""); setSuccessMessage(""); setIsDragging(false); }; const validateAndSetFile = (file) => { if (!file) return; if (!ACCEPTED_TYPES.includes(file.type)) { setErrorMessage("Please upload PNG, JPG, JPEG, or WEBP formats only."); setSuccessMessage(""); return; } if (file.size > 50 * 1024 * 1024) { setErrorMessage("Image must be smaller than 50 MB."); setSuccessMessage(""); return; } if (inputPreviewUrl) URL.revokeObjectURL(inputPreviewUrl); const newInputUrl = URL.createObjectURL(file); setSelectedFile(file); setInputPreviewUrl(newInputUrl); setErrorMessage(""); setSuccessMessage(""); clearResult(); }; const handleFileChange = (event) => { const file = event.target.files?.[0]; validateAndSetFile(file); }; const handleDrop = (event) => { event.preventDefault(); setIsDragging(false); const file = event.dataTransfer.files?.[0]; validateAndSetFile(file); }; const downloadEnhanced = () => { const a = document.createElement("a"); a.href = outputPreviewUrl; a.download = "enhanced-image.png"; document.body.appendChild(a); a.click(); document.body.removeChild(a); }; const enhanceImage = async () => { if (!selectedFile) { setErrorMessage("Please select an image first."); return; } setIsEnhancing(true); setErrorMessage(""); setSuccessMessage(""); const formData = new FormData(); formData.append("image", selectedFile); try { const response = await fetch(API_URL, { method: "POST", body: formData, }); if (!response.ok) { let serverMessage = "Enhancement failed. Please try again."; try { const errorBody = await response.json(); serverMessage = errorBody.error || serverMessage; } catch { serverMessage = `Enhancement failed with status ${response.status}.`; } throw new Error(serverMessage); } const blob = await response.blob(); const outputUrl = URL.createObjectURL(blob); if (outputPreviewUrl) URL.revokeObjectURL(outputPreviewUrl); setOutputPreviewUrl(outputUrl); setSuccessMessage("✓ Enhancement complete! Compare the images below."); } catch (error) { setErrorMessage(error.message || "An unexpected error occurred."); setSuccessMessage(""); } finally { setIsEnhancing(false); } }; return (
{/* Header */}

LumiGAN

Professional Low-Light Image Enhancement

{/* Main Content */}
{/* Hero Section */}

POWERED BY GENERATIVE AI

Brighten Your Darkness

Transform underexposed photos into stunning, well-lit images using advanced neural networks. Perfect for night photography, dimly lit interiors, and more.

🚀 Real-Time Processing 🎨 Neural Enhancement 📊 Before/After Comparison
{/* Main Layout: Upload + Results */}
{/* Upload Bar */}

Upload Image

{ event.preventDefault(); setIsDragging(true); }} onDragOver={(event) => event.preventDefault()} onDragLeave={() => setIsDragging(false)} onDrop={handleDrop} > {!inputPreviewUrl ? (
📸

Choose image

) : (
Preview

{selectedFile?.name}

{fileSizeInMb}

)}
{/* Alerts */} {errorMessage &&
{errorMessage}
} {successMessage &&
{successMessage}
} {/* Action Button */} {inputPreviewUrl && !outputPreviewUrl && ( )} {/* Reset Button */} {outputPreviewUrl && ( )}
{/* Results Section */} {outputPreviewUrl && (

Results

Original
Original
Enhanced
Enhanced
)}

Training Snapshot

Last epoch metrics (Source to Target samples)

source_discr
0.31682008504867554

Discriminator loss for the source domain. Lower means better real/fake separation.

target_discr
0.21157756447792053

Discriminator loss for the target domain. Lower means better real/fake separation.

source_gener
0.7708375255266825

Generator adversarial loss for source to target mapping. Lower is better.

target_gener
0.6412161688009897

Generator adversarial loss for target to source mapping. Lower is better.

recon_target_loss
0.05315116047859192

Cycle reconstruction loss for target domain consistency. Lower means better cycle match.

recon_source_loss
0.03360244259238243

Cycle reconstruction loss for source domain consistency. Lower means better cycle match.

ident_target
0.09518671035766602

Identity loss on target images. Encourages keeping colors and structure when already in target domain.

ident_source
0.049587182700634

Identity loss on source images. Encourages minimal change when already in source domain.

{/* Empty State */} {!inputPreviewUrl && !outputPreviewUrl && !errorMessage && (
🌙

Ready to enhance your low-light photos?

)}
{/* Footer */}
); } export default App;