Spaces:
Sleeping
Sleeping
| <!-- Main Flask/Jinja template for the StyleXfer web app. This page contains the upload form, preview areas, result display, examples section, footer, styling, and small client-side interactions. --> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>StyleXfer : Adaptive Neural Style Transfer</title> | |
| <!-- Bootstrap provides the responsive grid, navbar, cards, buttons, and utility classes used across the page. --> | |
| <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> | |
| <!-- Font Awesome supplies the brand, navigation, contact, and social media icons. --> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> | |
| <!-- Google Fonts: Fugaz One for display headings and Outfit for body/UI text. --> | |
| <link href="https://fonts.googleapis.com/css2?family=Fugaz+One&family=Outfit:wght@100..900&display=swap" rel="stylesheet"> | |
| <style> | |
| /* Base page theme and typography. */ | |
| html { | |
| scroll-behavior: smooth; | |
| } | |
| body { | |
| font-family: 'Outfit', sans-serif; | |
| background-color: #172033; | |
| color: #e2e8f0; | |
| min-height: 100vh; | |
| } | |
| /* Hero/header area shown below the fixed navigation bar. */ | |
| .header-section { | |
| padding: 5rem 0 3rem; | |
| text-align: center; | |
| position: relative; | |
| } | |
| .header-section h1 { | |
| font-family: 'Fugaz One', sans-serif; | |
| font-weight: 700; | |
| font-size: 3.5rem; | |
| background: linear-gradient(to right, #818cf8, #c084fc, #f472b6); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| text-shadow: 0 0 30px rgba(192, 132, 252, 0.3); | |
| margin-bottom: 1rem; | |
| } | |
| /* Shared card styling for upload panels, result preview, and examples. */ | |
| .card { | |
| background: rgba(30, 41, 59, 0.6); | |
| backdrop-filter: blur(12px); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| border-radius: 20px; | |
| overflow: hidden; | |
| box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); | |
| transition: transform 0.3s ease, box-shadow 0.3s ease; | |
| } | |
| .card:hover { | |
| box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); | |
| border-color: rgba(129, 140, 248, 0.3); | |
| } | |
| .card-header { | |
| background: rgba(255, 255, 255, 0.05); | |
| border-bottom: 1px solid rgba(255, 255, 255, 0.1); | |
| color: #f8fafc; | |
| font-family: 'Fugaz One', sans-serif; | |
| letter-spacing: 1px; | |
| text-transform: uppercase; | |
| font-size: 0.9rem; | |
| border-radius: 19px 19px 0 0; | |
| } | |
| /* Image preview boxes keep uploaded images visible without stretching them. */ | |
| .preview-image { | |
| width: 100%; | |
| height: 300px; | |
| object-fit: contain; | |
| background-color: #0f172a; | |
| border: 2px dashed #334155; | |
| border-radius: 10px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: #64748b; | |
| font-size: 1.2rem; | |
| overflow: hidden; | |
| } | |
| .preview-image img { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: contain; | |
| } | |
| /* Primary action button used for starting the style transfer. */ | |
| .btn-primary { | |
| background: linear-gradient(135deg, #6366f1 0%, #a855f7 100%); | |
| border: none; | |
| padding: 15px 50px; | |
| font-size: 1.2rem; | |
| font-family: 'Fugaz One', sans-serif; | |
| border-radius: 50px; | |
| transition: all 0.3s; | |
| text-transform: uppercase; | |
| letter-spacing: 2px; | |
| box-shadow: 0 10px 20px -10px rgba(168, 85, 247, 0.5); | |
| } | |
| .btn-primary:hover { | |
| background: linear-gradient(135deg, #4f46e5 0%, #9333ea 100%); | |
| box-shadow: 0 20px 30px -10px rgba(168, 85, 247, 0.6); | |
| } | |
| /* Glow animation draws attention to the submit button. */ | |
| .btn-glow { | |
| animation: glowing 2s infinite; | |
| } | |
| @keyframes glowing { | |
| 0% { box-shadow: 0 0 5px #a855f7; } | |
| 50% { box-shadow: 0 0 20px #a855f7, 0 0 10px #6366f1; } | |
| 100% { box-shadow: 0 0 5px #a855f7; } | |
| } | |
| /* Custom range slider for controlling AdaIN style strength (alpha). */ | |
| .range-wrap { | |
| position: relative; | |
| margin: 2rem auto; | |
| width: 100%; | |
| max-width: 90%; | |
| } | |
| #alphaRange { | |
| -webkit-appearance: none; | |
| width: 100%; | |
| height: 12px; | |
| background: rgba(30, 41, 59, 0.8); | |
| border-radius: 15px; | |
| outline: none; | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| box-shadow: inset 0 2px 5px rgba(0,0,0,0.5); | |
| transition: background 0.1s; | |
| } | |
| #alphaRange::-webkit-slider-runnable-track { | |
| width: 100%; | |
| height: 100%; | |
| cursor: pointer; | |
| background: transparent; | |
| } | |
| #alphaRange::-webkit-slider-thumb { | |
| -webkit-appearance: none; | |
| height: 24px; | |
| width: 24px; | |
| border-radius: 50%; | |
| background: #fff; | |
| cursor: pointer; | |
| margin-top: -6px; | |
| box-shadow: 0 0 10px #c084fc, 0 0 20px #818cf8; | |
| border: 3px solid #1e293b; | |
| transition: transform 0.2s, box-shadow 0.2s; | |
| } | |
| #alphaRange::-webkit-slider-thumb:hover { | |
| box-shadow: 0 0 15px #c084fc, 0 0 30px #818cf8; | |
| } | |
| .range-value { | |
| position: absolute; | |
| top: -40px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| background: linear-gradient(135deg, #6366f1 0%, #a855f7 100%); | |
| color: white; | |
| padding: 4px 12px; | |
| border-radius: 15px; | |
| font-family: 'Fugaz One', sans-serif; | |
| font-size: 0.85rem; | |
| box-shadow: 0 5px 15px rgba(168, 85, 247, 0.4); | |
| opacity: 0; | |
| transition: opacity 0.3s; | |
| pointer-events: none; | |
| } | |
| .range-value::after { | |
| content: ''; | |
| position: absolute; | |
| bottom: -5px; | |
| left: 50%; | |
| margin-left: -5px; | |
| border-width: 5px 5px 0; | |
| border-style: solid; | |
| border-color: #a855f7 transparent transparent transparent; | |
| } | |
| .range-wrap:hover .range-value { | |
| opacity: 1; | |
| } | |
| /* File input styling for the content and style image upload controls. */ | |
| .form-control { | |
| background-color: #1e293b; | |
| border: 1px solid #334155; | |
| color: #f8fafc; | |
| } | |
| .form-control:focus { | |
| background-color: #1e293b; | |
| border-color: #818cf8; | |
| color: #f8fafc; | |
| box-shadow: 0 0 0 0.25rem rgba(129, 140, 248, 0.25); | |
| } | |
| .form-control::file-selector-button { | |
| background-color: #334155; | |
| color: #f8fafc; | |
| border: none; | |
| padding: 0.375rem 0.75rem; | |
| margin-right: 0.75rem; | |
| border-radius: 0.25rem; | |
| transition: background-color 0.2s; | |
| } | |
| .form-control::file-selector-button:hover { | |
| background-color: #475569; | |
| } | |
| /* Custom scrollbar to match the dark visual theme. */ | |
| ::-webkit-scrollbar { | |
| width: 10px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: #0f172a; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: #334155; | |
| border-radius: 5px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: #818cf8; | |
| } | |
| /* Reusable fade-in animation for major content sections. */ | |
| .fade-in-up { | |
| animation: fadeInUp 0.8s ease-out forwards; | |
| opacity: 0; | |
| transform: translateY(30px); | |
| } | |
| @keyframes fadeInUp { | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| /* Full-page loading overlay shown while the server processes the submitted images. */ | |
| .loader-overlay { | |
| position: fixed; | |
| inset: 0; | |
| z-index: 9999; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| flex-direction: column; | |
| gap: 1rem; | |
| background: rgba(15, 23, 42, 0.92); | |
| opacity: 0; | |
| pointer-events: none; | |
| transition: opacity 0.25s ease; | |
| } | |
| .loader-overlay.active { | |
| opacity: 1; | |
| pointer-events: all; | |
| } | |
| .style-spinner { | |
| width: 72px; | |
| height: 72px; | |
| border: 4px solid rgba(129, 140, 248, 0.18); | |
| border-top-color: #818cf8; | |
| border-right-color: #c084fc; | |
| border-radius: 50%; | |
| animation: spin 0.9s linear infinite; | |
| box-shadow: 0 0 18px rgba(129, 140, 248, 0.35); | |
| } | |
| .loader-overlay h3 { | |
| color: #f8fafc; | |
| font-family: 'Fugaz One', sans-serif; | |
| letter-spacing: 2px; | |
| margin: 0; | |
| } | |
| .loader-overlay p { | |
| color: #cbd5e1; | |
| margin: 0; | |
| } | |
| @keyframes spin { | |
| to { | |
| transform: rotate(360deg); | |
| } | |
| } | |
| /* Fixed top navigation and brand mark. */ | |
| .navbar { | |
| background: rgba(15, 23, 42, 0.8); | |
| backdrop-filter: blur(10px); | |
| border-bottom: 1px solid rgba(255, 255, 255, 0.05); | |
| } | |
| .navbar-brand { | |
| font-family: 'Fugaz One', sans-serif; | |
| font-weight: 700; | |
| font-size: 1.5rem; | |
| background: linear-gradient(to right, #818cf8, #c084fc); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 0.55rem; | |
| } | |
| .brand-logo { | |
| position: relative; | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| width: 2rem; | |
| height: 2rem; | |
| border-radius: 50%; | |
| background: rgba(129, 140, 248, 0.14); | |
| border: 1px solid rgba(165, 180, 252, 0.45); | |
| color: #c4b5fd; | |
| -webkit-text-fill-color: #c4b5fd; | |
| box-shadow: 0 0 14px rgba(129, 140, 248, 0.18); | |
| flex: 0 0 auto; | |
| } | |
| .brand-logo .fa-wave-square { | |
| font-size: 0.95rem; | |
| } | |
| .brand-logo .fa-circle-nodes { | |
| position: absolute; | |
| right: -0.22rem; | |
| bottom: -0.12rem; | |
| font-size: 0.62rem; | |
| color: #93c5fd; | |
| -webkit-text-fill-color: #93c5fd; | |
| } | |
| .nav-link { | |
| color: #e2e8f0 ; | |
| font-weight: 500; | |
| margin-left: 1rem; | |
| transition: color 0.3s; | |
| position: relative; | |
| } | |
| .nav-link:hover { | |
| color: #818cf8 ; | |
| } | |
| .nav-link::after { | |
| content: ''; | |
| position: absolute; | |
| width: 0; | |
| height: 2px; | |
| bottom: 0; | |
| left: 0; | |
| background-color: #818cf8; | |
| transition: width 0.3s; | |
| } | |
| .nav-link:hover::after { | |
| width: 100%; | |
| } | |
| /* Footer, contact details, and social links. */ | |
| .footer-section { | |
| background: rgba(15, 23, 42, 0.95); | |
| border-top: 1px solid rgba(255, 255, 255, 0.05); | |
| padding: 4rem 0 2rem; | |
| margin-top: 5rem; | |
| color: #94a3b8; | |
| scroll-margin-top: 90px; | |
| } | |
| .footer-section h5 { | |
| color: #f8fafc; | |
| font-family: 'Fugaz One', sans-serif; | |
| margin-bottom: 1.5rem; | |
| font-size: 1.1rem; | |
| } | |
| .footer-brand { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 0.55rem; | |
| } | |
| .footer-link { | |
| color: #94a3b8; | |
| text-decoration: none; | |
| transition: color 0.3s; | |
| display: block; | |
| margin-bottom: 0.8rem; | |
| } | |
| .footer-link:hover { | |
| color: #818cf8; | |
| } | |
| .social-icon { | |
| width: 40px; | |
| height: 40px; | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| background: rgba(255,255,255,0.05); | |
| border-radius: 50%; | |
| color: #f8fafc; | |
| margin-right: 10px; | |
| transition: all 0.3s; | |
| text-decoration: none; | |
| } | |
| .social-icon:hover { | |
| background: #818cf8; | |
| color: white; | |
| } | |
| /* Example gallery showing content + style image pairs and their generated output. */ | |
| .examples-section { | |
| margin-top: 4rem; | |
| scroll-margin-top: 90px; | |
| } | |
| .examples-section .examples-note { | |
| color: #cbd5e1; | |
| } | |
| .examples-section .example-image { | |
| width: 100%; | |
| height: 220px; | |
| object-fit: contain; | |
| border-radius: 10px; | |
| border: 1px solid rgba(255, 255, 255, 0.12); | |
| background: rgba(15, 23, 42, 0.8); | |
| } | |
| .examples-section .example-role { | |
| font-size: 0.82rem; | |
| font-weight: 700; | |
| letter-spacing: 0.08em; | |
| color: #cbd5e1; | |
| margin-bottom: 0.45rem; | |
| text-transform: uppercase; | |
| } | |
| .examples-section .example-flow { | |
| display: flex; | |
| align-items: center; | |
| gap: 1.5rem; | |
| padding: 1.25rem; | |
| border-radius: 18px; | |
| background: rgba(15, 23, 42, 0.45); | |
| border: 1px solid rgba(255, 255, 255, 0.08); | |
| } | |
| .examples-section .example-inputs { | |
| flex: 1; | |
| display: grid; | |
| gap: 1rem; | |
| } | |
| .examples-section .example-output { | |
| flex: 1; | |
| } | |
| .examples-section .example-arrow { | |
| color: #a5b4fc; | |
| font-size: 1.75rem; | |
| font-weight: 700; | |
| line-height: 1; | |
| } | |
| @media (max-width: 767.98px) { | |
| .examples-section .example-flow { | |
| flex-direction: column; | |
| } | |
| .examples-section .example-arrow { | |
| transform: rotate(90deg); | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Fixed navigation with links to major page sections. --> | |
| <nav class="navbar navbar-expand-lg fixed-top"> | |
| <div class="container"> | |
| <a class="navbar-brand" href="#"> | |
| <span class="brand-logo" aria-hidden="true"> | |
| <i class="fas fa-wave-square"></i> | |
| <i class="fas fa-circle-nodes"></i> | |
| </span> | |
| <span>StyleXfer</span> | |
| </a> | |
| <button class="navbar-toggler navbar-dark" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"> | |
| <span class="navbar-toggler-icon"></span> | |
| </button> | |
| <div class="collapse navbar-collapse" id="navbarNav"> | |
| <ul class="navbar-nav ms-auto"> | |
| <li class="nav-item"><a class="nav-link" href="#">Home</a></li> | |
| <li class="nav-item"><a class="nav-link" href="#examples">Examples</a></li> | |
| <li class="nav-item"><a class="nav-link" href="#contact">Contact</a></li> | |
| </ul> | |
| <a href="#nst_section" class="btn btn-sm btn-outline-light ms-3 rounded-pill px-4">Try It</a> | |
| </div> | |
| </div> | |
| </nav> | |
| <!-- App title and short description. --> | |
| <div class="header-section"> | |
| <div class="container"> | |
| <h1 class="display-4 fw-bold">StyleXfer</h1> | |
| <h3 style="color: white; margin: 0 auto; text-align: center;">Adaptive Neural Style Transfer Using AdaIN</h3> | |
| </div> | |
| </div> | |
| <!-- Upload form: Flask-WTF renders CSRF protection and file/range/submit fields. --> | |
| <div class="container mb-5 fade-in-up" id="nst_section"> | |
| <!-- # method="post" → the form will send data to the server via POST (not GET). --> | |
| <!-- enctype="multipart/form-data" → required when uploading files (images, etc.). Without this, file uploads won’t work. --> | |
| <form method="post" enctype="multipart/form-data" id="styleTransferForm"> | |
| <!-- This is a Flask‑WTF helper. It automatically renders hidden fields needed for the form, most importantly the CSRF token. --> | |
| <!-- CSRF (Cross‑Site Request Forgery) protection ensures that only requests generated from your app are accepted, preventing malicious submissions. --> | |
| <!-- So, when you use form.hidden_tag(), Flask‑WTF inserts something like: --> | |
| <!-- <input id="csrf_token" name="csrf_token" type="hidden" value="random_secure_token"> --> | |
| <!-- This hidden token is checked on the server side when the form is submitted. --> | |
| {{ form.hidden_tag() }} | |
| <div class="row g-4"> | |
| <!-- Content image upload and live preview. --> | |
| <div class="col-md-6"> | |
| <div class="card h-100"> | |
| <div class="card-header text-center py-3">Content Source</div> | |
| <div class="card-body text-center"> | |
| <div class="preview-image mb-3" id="contentPreview"> | |
| {% if content_image %} | |
| <!-- {{ url_for('send_image', filename=content_image) }} :- This is Jinja2 template syntax (used in Flask). --> | |
| <!-- url_for('send_image', filename=content_image) dynamically generates the URL for the Flask route named send_image. --> | |
| <!-- The filename=content_image part passes the image filename as a parameter to that route. --> | |
| <img src="{{ url_for('send_image', filename=content_image) }}" alt="Content Image"> | |
| {% else %} | |
| <span>Select Content Image</span> | |
| {% endif %} | |
| </div> | |
| {{ form.content(class="form-control", onchange="previewFile(this, 'contentPreview')") }} | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Style reference upload and live preview. --> | |
| <div class="col-md-6"> | |
| <div class="card h-100"> | |
| <div class="card-header text-center py-3">Style Reference</div> | |
| <div class="card-body text-center"> | |
| <div class="preview-image mb-3" id="stylePreview"> | |
| {% if style_image %} | |
| <img src="{{ url_for('send_image', filename=style_image) }}" alt="Style Image"> | |
| {% else %} | |
| <span>Select Style Image</span> | |
| {% endif %} | |
| </div> | |
| {{ form.style(class="form-control", onchange="previewFile(this, 'stylePreview')") }} | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Style-strength control and submit button. --> | |
| <div class="row justify-content-center mt-4"> | |
| <div class="col-md-8"> | |
| <div class="range-wrap"> | |
| <label for="alpha" class="form-label fw-bold mb-3 d-block text-center" style="font-family: 'Fugaz One', sans-serif; letter-spacing: 1px; color: #a855f7;">STYLE STRENGTH</label> | |
| <div class="position-relative"> | |
| <div class="range-value" id="rangeValue">1.0</div> | |
| {{ form.alpha(class="form-control-range", id="alphaRange", type="range", min="0", max="1", step="0.1") }} | |
| </div> | |
| </div> | |
| <div class="text-center mt-5"> | |
| {{ form.submit(class="btn btn-primary shadow-lg btn-glow") }} | |
| </div> | |
| </div> | |
| </div> | |
| </form> | |
| <!-- Render the processed output only after the backend returns a result image filename. --> | |
| {% if result_image %} | |
| <div class="row justify-content-center mt-5" id="resultSection"> | |
| <div class="col-md-8"> | |
| <div class="card border-success"> | |
| <div class="card-header bg-success text-white fw-bold text-center py-3" style="background: linear-gradient(135deg, #10b981 0%, #059669 100%);">Stylized Result</div> | |
| <div class="card-body p-0"> | |
| <img src="{{ url_for('send_image', filename=result_image) }}" class="img-fluid w-100" alt="Stylized Image" style="border-radius: 0 0 20px 20px;"> | |
| </div> | |
| <div class="card-footer text-center" style="background: transparent; border-top: none; padding-bottom: 20px;"> | |
| <a href="{{ url_for('send_image', filename=result_image) }}" download class="btn btn-outline-light mt-2">Download Result</a> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| {% endif %} | |
| <!-- Show backend validation or processing errors near the form. --> | |
| {% if error %} | |
| <div class="alert alert-danger mt-4 text-center" role="alert" style="background: rgba(220, 38, 38, 0.2); border-color: #dc2626; color: #fecaca;"> | |
| <strong>Error:</strong> {{ error }} | |
| </div> | |
| {% endif %} | |
| </div> | |
| <!-- Static examples served from the configured examples folder. --> | |
| <section class="container examples-section fade-in-up" id="examples"> | |
| <div class="card"> | |
| <div class="card-header text-center py-3">Examples</div> | |
| <div class="card-body"> | |
| <p class="examples-note text-center mb-4">Each example shows the content image, the style below it, and the final output beside them.</p> | |
| <div class="row g-4"> | |
| <div class="col-12"> | |
| <div class="example-flow"> | |
| <div class="example-inputs"> | |
| <div> | |
| <div class="example-role text-center">Content Image</div> | |
| <img src="{{ url_for('send_example', filename='brad_pitt.jpg') }}" alt="Brad Pitt Content" class="example-image"> | |
| </div> | |
| <div> | |
| <div class="example-role text-center">Style Image</div> | |
| <img src="{{ url_for('send_example', filename='sketch.png') }}" alt="Sketch Style" class="example-image"> | |
| </div> | |
| </div> | |
| <div class="example-arrow">-></div> | |
| <div class="example-output"> | |
| <div class="example-role text-center">Output</div> | |
| <img src="{{ url_for('send_example', filename='stylized_brad_pitt.jpg') }}" alt="Brad Pitt Sketch Result" class="example-image"> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="col-12"> | |
| <div class="example-flow"> | |
| <div class="example-inputs"> | |
| <div> | |
| <div class="example-role text-center">Content Image</div> | |
| <img src="{{ url_for('send_example', filename='brad_pitt.jpg') }}" alt="Brad Pitt Content" class="example-image"> | |
| </div> | |
| <div> | |
| <div class="example-role text-center">Style Image</div> | |
| <img src="{{ url_for('send_example', filename='picasso_seated_nude_hr.jpg') }}" alt="Picasso Seated Nude Style" class="example-image"> | |
| </div> | |
| </div> | |
| <div class="example-arrow">-></div> | |
| <div class="example-output"> | |
| <div class="example-role text-center">Output</div> | |
| <img src="{{ url_for('send_example', filename='stylized_brad_pitt_1.jpg') }}" alt="Brad Pitt Picasso Result" class="example-image"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Footer with project summary, quick links, contact email, and social profiles. --> | |
| <footer class="footer-section" id="contact"> | |
| <div class="container"> | |
| <div class="row g-4"> | |
| <div class="col-lg-4 col-md-6"> | |
| <h5 class="footer-brand"> | |
| <span class="brand-logo" aria-hidden="true"> | |
| <i class="fas fa-wave-square"></i> | |
| <i class="fas fa-circle-nodes"></i> | |
| </span> | |
| <span>StyleXfer</span> | |
| </h5> | |
| <p class="mb-4">"Transform reality into art. Apply any artistic style to your images with AI-powered neural style transfer. Blend inspiration with imagination—instantly reimagine your photos through advanced neural technology."</p> | |
| </div> | |
| <div class="col-lg-2 col-md-6"> | |
| <h5>Quick Links</h5> | |
| <a href="#" class="footer-link">Home</a> | |
| <a href="#examples" class="footer-link">Examples</a> | |
| </div> | |
| <div class="col-lg-3 col-md-6"> | |
| <h5>Contact</h5> | |
| <p class="mb-2"><i class="fas fa-envelope me-2"></i> arpitpal16112@gmail.com</p> | |
| </div> | |
| <div class="col-lg-3 col-md-6"> | |
| <h5>Follow</h5> | |
| <div class="d-flex mb-3"> | |
| <a href="https://www.linkedin.com/in/arpitpal0711" class="social-icon"><i class="fab fa-linkedin-in"></i></a> | |
| <a href="https://www.instagram.com/arpit_p_07/" class="social-icon"><i class="fab fa-instagram"></i></a> | |
| <a href="https://github.com/AP16112" class="social-icon"><i class="fab fa-github"></i></a> | |
| </div> | |
| </div> | |
| </div> | |
| <hr class="my-4" style="border-color: rgba(255,255,255,0.1);"> | |
| <div class="text-center text-muted small"> | |
| <p style='font-weight: bold; color: white;'> Created with ❤️ by <span style="color: #facc15;">Arpit Pal</span> </p> | |
| </div> | |
| </div> | |
| </footer> | |
| <!-- Processing indicator displayed while the form submission is running. --> | |
| <div class="loader-overlay" id="loader" aria-live="polite" aria-hidden="true"> | |
| <div class="style-spinner"></div> | |
| <h3>PROCESSING</h3> | |
| <p>Creating your stylized image...</p> | |
| </div> | |
| <!-- Bootstrap JavaScript enables the responsive navbar collapse behavior. --> | |
| <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> | |
| <script> | |
| // Display a selected local image immediately before the form is submitted. | |
| function previewFile(input, previewId) { | |
| const preview = document.getElementById(previewId); | |
| const file = input.files[0]; | |
| const reader = new FileReader(); | |
| reader.addEventListener("load", function () { | |
| // Convert the selected file to a temporary base64 URL for previewing. | |
| preview.innerHTML = `<img src="${reader.result}" alt="Image Preview">`; | |
| }, false); | |
| if (file) { | |
| reader.readAsDataURL(file); | |
| } | |
| } | |
| // Initialize result scrolling, slider UI, and loading overlay behavior after the page loads. | |
| window.onload = function() { | |
| if(document.getElementById('resultSection')) { | |
| document.getElementById('resultSection').scrollIntoView({behavior: "smooth"}); | |
| } | |
| // Range Slider Logic | |
| const form = document.getElementById('styleTransferForm'); | |
| const loader = document.getElementById('loader'); | |
| const range = document.getElementById('alphaRange'); | |
| const rangeValue = document.getElementById('rangeValue'); | |
| function updateRange() { | |
| const val = range.value; | |
| const min = range.min || 0; | |
| const max = range.max || 1; | |
| const percentage = ((val - min) * 100) / (max - min); | |
| rangeValue.textContent = val; | |
| // Adjust position of the bubble to follow the thumb | |
| const thumbWidth = 24; | |
| const offset = thumbWidth/2 - (percentage * thumbWidth / 100); | |
| rangeValue.style.left = `calc(${percentage}% + ${offset}px)`; | |
| // Fill the slider track up to the current value. | |
| range.style.background = `linear-gradient(to right, #818cf8 0%, #c084fc ${percentage}%, rgba(30, 41, 59, 0.8) ${percentage}%, rgba(30, 41, 59, 0.8) 100%)`; | |
| } | |
| if(range) { | |
| range.addEventListener('input', updateRange); | |
| updateRange(); | |
| } | |
| if(form && loader) { | |
| form.addEventListener('submit', function() { | |
| loader.classList.add('active'); | |
| loader.setAttribute('aria-hidden', 'false'); | |
| }); | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> | |