Spaces:
Running
Running
Download index.html from KTXStudio/MinecraftSkinAi: direct link, hf CLI and curl.
- Browser
- Download file 8.66 kB
-
https://huggingface.co/spaces/KTXStudio/MinecraftSkinAi/resolve/main/index.html
- Command line
-
hf download hf://spaces/KTXStudio/MinecraftSkinAi/index.html
-
curl -L -o index.html https://huggingface.co/spaces/KTXStudio/MinecraftSkinAi/resolve/main/index.html
8.66 kB
| <html lang="fr"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Minecraft HD Skin AI (Static Web)</title> | |
| <!-- Chargement de la version stable d'ONNX Runtime Web --> | |
| <script src="https://jsdelivr.net"></script> | |
| <style> | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| background: #121212; | |
| color: #ffffff; | |
| text-align: center; | |
| padding: 20px; | |
| margin: 0; | |
| } | |
| .container { | |
| max-width: 600px; | |
| margin: 40px auto; | |
| background: #1e1e1e; | |
| padding: 30px; | |
| border-radius: 12px; | |
| box-shadow: 0 4px 15px rgba(0,0,0,0.5); | |
| } | |
| h2 { color: #4CAF50; margin-top: 0; } | |
| input[type="file"] { display: none; } | |
| .custom-file-upload { | |
| display: inline-block; | |
| padding: 12px 24px; | |
| cursor: pointer; | |
| background: #4CAF50; | |
| color: white; | |
| border-radius: 5px; | |
| font-weight: bold; | |
| margin: 20px 0; | |
| transition: background 0.3s; | |
| } | |
| .custom-file-upload.disabled { | |
| background: #444; | |
| cursor: not-allowed; | |
| opacity: 0.6; | |
| } | |
| #status { | |
| font-weight: bold; | |
| color: #aaa; | |
| margin-bottom: 20px; | |
| padding: 10px; | |
| border-radius: 5px; | |
| background: #252525; | |
| } | |
| #result-container { | |
| margin-top: 25px; | |
| display: none; | |
| } | |
| #result { | |
| width: 256px; | |
| height: 256px; | |
| image-rendering: pixelated; | |
| image-rendering: crisp-edges; | |
| border: 3px solid #4CAF50; | |
| background-image: linear-gradient(45deg, #2b2b2b 25%, transparent 25%), | |
| linear-gradient(-45deg, #2b2b2b 25%, transparent 25%), | |
| linear-gradient(45deg, transparent 75%, #2b2b2b 75%), | |
| linear-gradient(-45deg, transparent 75%, #2b2b2b 75%); | |
| background-size: 20px 20px; | |
| background-position: 0 0, 0 10px, 10px -10px, -10px 0; | |
| border-radius: 8px; | |
| } | |
| .download-btn { | |
| display: inline-block; | |
| margin-top: 15px; | |
| padding: 10px 20px; | |
| background: #008CBA; | |
| color: white; | |
| text-decoration: none; | |
| border-radius: 5px; | |
| font-weight: bold; | |
| transition: background 0.3s; | |
| } | |
| .download-btn:hover { background: #007B9A; } | |
| canvas { display: none; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h2>🎨 Minecraft HD Skin AI Generator</h2> | |
| <p>Cette IA déterministe s'exécute localement sur votre navigateur.</p> | |
| <div id="status">⏳ Initialisation du moteur WebAssembly (WASM)...</div> | |
| <label for="imageLoader" id="uploadLabel" class="custom-file-upload disabled"> | |
| Sélectionner une Image / Rendu 3D | |
| </label> | |
| <input type="file" id="imageLoader" accept="image/*" disabled> | |
| <div id="result-container"> | |
| <h3>✨ Skin HD Généré (128x128)</h3> | |
| <img id="result" alt="Skin généré"><br> | |
| <a id="downloadLink" class="download-btn" download="skin_hd_ai.png">⬇️ Télécharger le Skin</a> | |
| </div> | |
| <!-- Zone de traitement mémoire masquée --> | |
| <canvas id="inputCanvas" width="224" height="224"></canvas> | |
| <canvas id="outputCanvas" width="128" height="128"></canvas> | |
| </div> | |
| <script> | |
| let session = null; | |
| const statusDiv = document.getElementById('status'); | |
| const fileInput = document.getElementById('imageLoader'); | |
| const uploadLabel = document.getElementById('uploadLabel'); | |
| const resultContainer = document.getElementById('result-container'); | |
| const resultImg = document.getElementById('result'); | |
| const downloadLink = document.getElementById('downloadLink'); | |
| // Configuration des runtimes binaires pour ONNX Runtime Web via CDN | |
| ort.env.wasm.numThreads = 1; | |
| ort.env.wasm.wasmPaths = "https://jsdelivr.net"; | |
| // 1. Initialisation de l'IA (Chargement du fichier .onnx local au Space Static) | |
| async function initAI() { | |
| try { | |
| session = await ort.InferenceSession.create('./vit_minecraft_model.onnx', { | |
| executionProviders: ['wasm'] | |
| }); | |
| statusDiv.style.color = "#4CAF50"; | |
| statusDiv.innerText = "✅ Modèle ONNX chargé ! Votre IA est prête."; | |
| fileInput.disabled = false; | |
| uploadLabel.classList.remove('disabled'); | |
| } catch (e) { | |
| statusDiv.style.color = "#f44336"; | |
| statusDiv.innerText = "❌ Échec. Vérifiez que 'vit_minecraft_model.onnx' est bien présent dans votre Space."; | |
| console.error(e); | |
| } | |
| } | |
| // 2. Traitement d'image et exécution | |
| fileInput.addEventListener('change', handleImage, false); | |
| async function handleImage(e) { | |
| if (!session || e.target.files.length === 0) return; | |
| statusDiv.style.color = "#aaa"; | |
| statusDiv.innerText = "⏳ Inférence en cours sur votre processeur..."; | |
| resultContainer.style.display = "none"; | |
| const reader = new FileReader(); | |
| reader.onload = function(event) { | |
| const img = new Image(); | |
| img.onload = async function() { | |
| // Redimensionnement de l'entrée à 224x224 (Strict pour ViT) | |
| const ctxIn = document.getElementById('inputCanvas').getContext('2d'); | |
| ctxIn.drawImage(img, 0, 0, 224, 224); | |
| // Préparation et normalisation du tenseur d'entrée | |
| const imgData = ctxIn.getImageData(0, 0, 224, 224).data; | |
| const float32Array = new Float32Array(1 * 3 * 224 * 224); | |
| // Alignement mathématique sur les canaux CHW exigés par PyTorch | |
| for (let i = 0; i < 224 * 224; i++) { | |
| float32Array[i] = ((imgData[i * 4] / 255.0) - 0.5) / 0.5; // R | |
| float32Array[i + 224*224] = ((imgData[i * 4 + 1] / 255.0) - 0.5) / 0.5; // G | |
| float32Array[i + 2*224*224] = ((imgData[i * 4 + 2] / 255.0) - 0.5) / 0.5; // B | |
| } | |
| try { | |
| // Exécution de l'inférence | |
| const inputTensor = new ort.Tensor('float32', float32Array, [1, 3, 224, 224]); | |
| const feeds = { input: inputTensor }; | |
| const results = await session.run(feeds); | |
| const outputData = results.output.data; | |
| // Reconstruction des pixels au format image 128x128 | |
| const ctxOut = document.getElementById('outputCanvas').getContext('2d'); | |
| const outImgData = ctxOut.createImageData(128, 128); | |
| for (let i = 0; i < 128 * 128; i++) { | |
| outImgData.data[i * 4] = Math.min(Math.max(outputData[i] * 255, 0), 255); // R | |
| outImgData.data[i * 4 + 1] = Math.min(Math.max(outputData[i + 128*128] * 255, 0), 255); // G | |
| outImgData.data[i * 4 + 2] = Math.min(Math.max(outputData[i + 2*128*128] * 255, 0), 255); // B | |
| outImgData.data[i * 4 + 3] = 255; // A (Pleine opacité) | |
| } | |
| ctxOut.putImageData(outImgData, 0, 0); | |
| // Génération du lien de sortie et affichage | |
| const finalDataUrl = document.getElementById('outputCanvas').toDataURL("image/png"); | |
| resultImg.src = finalDataUrl; | |
| downloadLink.href = finalDataUrl; | |
| statusDiv.style.color = "#4CAF50"; | |
| statusDiv.innerText = "✨ Génération terminée !"; | |
| resultContainer.style.display = "block"; | |
| } catch (err) { | |
| statusDiv.style.color = "#f44336"; | |
| statusDiv.innerText = "❌ Erreur durant le calcul mathématique de l'IA."; | |
| console.error(err); | |
| } | |
| } | |
| img.src = event.target.result; | |
| } | |
| reader.readAsDataURL(e.target.files); | |
| } | |
| // Lancement automatique du script | |
| initAI(); | |
| </script> | |
| </body> | |
| </html> | |