MinecraftSkinAi / index.html
lea97338's picture
Update index.html
5a786e3 verified
Raw History Blame Contribute Delete
8.66 kB
<!DOCTYPE html>
<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>