easyocr / index.html
roque99cheroA's picture
Upload index.html
8c3063a verified
Raw
History Blame Contribute Delete
11.4 kB
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EasyOCR - Reconocimiento Rápido</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Segoe UI', sans-serif;
background: linear-gradient(135deg, #0f2027, #203a43, #2c5364);
min-height: 100vh;
color: #fff;
}
.container { max-width: 900px; margin: 0 auto; padding: 20px; }
.header { text-align: center; padding: 40px 0; }
.header h1 {
font-size: 2.5rem;
background: linear-gradient(90deg, #00ff88, #00d4ff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.header .badge {
background: #00ff88;
color: #000;
padding: 5px 15px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: bold;
margin-top: 10px;
display: inline-block;
}
.status-bar {
display: flex;
justify-content: center;
gap: 20px;
margin-bottom: 30px;
}
.status-item {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 20px;
background: rgba(255,255,255,0.1);
border-radius: 20px;
font-size: 0.85rem;
}
.status-dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: #ff4444;
}
.status-dot.online {
background: #00ff88;
box-shadow: 0 0 10px #00ff88;
}
.upload-section {
background: rgba(255,255,255,0.05);
border: 2px dashed rgba(255,255,255,0.2);
border-radius: 20px;
padding: 40px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
}
.upload-section:hover {
border-color: #00ff88;
background: rgba(0, 255, 136, 0.05);
}
.upload-icon { font-size: 4rem; margin-bottom: 20px; }
#fileInput { display: none; }
.btn {
background: linear-gradient(90deg, #00ff88, #00d4ff);
border: none;
padding: 15px 40px;
border-radius: 30px;
color: #000;
font-weight: bold;
cursor: pointer;
margin-top: 20px;
transition: transform 0.2s;
}
.btn:hover { transform: translateY(-2px); }
.preview-section { margin-top: 30px; display: none; }
.preview-section.show { display: block; }
.preview-container { display: flex; gap: 20px; flex-wrap: wrap; }
.preview-image, .results-section {
flex: 1;
min-width: 300px;
background: rgba(255,255,255,0.05);
border-radius: 15px;
padding: 20px;
}
.preview-image img { max-width: 100%; border-radius: 10px; }
.preview-image h4 { margin-bottom: 15px; color: #00d4ff; }
.results-section h4 { margin-bottom: 15px; color: #00ff88; }
.result-text {
background: rgba(0,0,0,0.3);
padding: 20px;
border-radius: 10px;
font-size: 1.1rem;
line-height: 1.6;
min-height: 100px;
white-space: pre-wrap;
}
.confidence-bar { margin-top: 20px; }
.confidence-bar label { display: block; margin-bottom: 8px; color: #aaa; }
.progress {
height: 10px;
background: rgba(255,255,255,0.1);
border-radius: 5px;
overflow: hidden;
}
.progress-bar {
height: 100%;
background: linear-gradient(90deg, #00ff88, #00d4ff);
transition: width 0.5s;
}
.confidence-value { text-align: right; margin-top: 5px; color: #00ff88; }
.loading { display: none; text-align: center; padding: 40px; }
.loading.show { display: block; }
.spinner {
width: 50px;
height: 50px;
border: 4px solid rgba(255,255,255,0.1);
border-top-color: #00ff88;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
}
@keyframes spin { to { transform: rotate(360deg); } }
.error {
background: rgba(255, 68, 68, 0.2);
border: 1px solid #ff4444;
padding: 20px;
border-radius: 10px;
margin-top: 20px;
display: none;
}
.error.show { display: block; }
.lines-detail { margin-top: 20px; }
.lines-detail h5 { color: #888; margin-bottom: 10px; }
.line-item {
display: flex;
justify-content: space-between;
padding: 8px 12px;
background: rgba(0,0,0,0.2);
border-radius: 8px;
margin-bottom: 6px;
font-size: 0.9rem;
}
.line-conf { color: #00d4ff; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>EasyOCR</h1>
<div class="badge">RÁPIDO ~2-5 seg</div>
<p style="color:#aaa; margin-top:15px;">Reconocimiento de texto - Español e Inglés</p>
</div>
<div class="status-bar">
<div class="status-item">
<div class="status-dot" id="statusDot"></div>
<span id="statusText">Verificando...</span>
</div>
</div>
<div class="upload-section" id="uploadArea">
<div class="upload-icon">📄</div>
<h3>Arrastra tu imagen aquí</h3>
<p style="color:#888;">o haz clic para seleccionar</p>
<input type="file" id="fileInput" accept="image/*">
<button class="btn" id="selectBtn">Seleccionar Imagen</button>
</div>
<div class="loading" id="loading">
<div class="spinner"></div>
<p>Procesando con EasyOCR...</p>
</div>
<div class="error" id="error">
<strong>Error:</strong> <span id="errorText"></span>
</div>
<div class="preview-section" id="previewSection">
<div class="preview-container">
<div class="preview-image">
<h4>📷 Imagen</h4>
<img id="previewImg" src="" alt="Preview">
</div>
<div class="results-section">
<h4>✨ Texto Reconocido</h4>
<div class="result-text" id="resultText">-</div>
<div class="confidence-bar">
<label>Confianza</label>
<div class="progress">
<div class="progress-bar" id="confidenceBar" style="width:0%"></div>
</div>
<div class="confidence-value" id="confidenceValue">0%</div>
</div>
<div class="lines-detail" id="linesDetail">
<h5>Detalle:</h5>
<div id="linesList"></div>
</div>
</div>
</div>
</div>
</div>
<script>
const API_URL = window.location.origin;
const uploadArea = document.getElementById('uploadArea');
const fileInput = document.getElementById('fileInput');
const selectBtn = document.getElementById('selectBtn');
const loading = document.getElementById('loading');
const error = document.getElementById('error');
const errorText = document.getElementById('errorText');
const previewSection = document.getElementById('previewSection');
const previewImg = document.getElementById('previewImg');
const resultText = document.getElementById('resultText');
const confidenceBar = document.getElementById('confidenceBar');
const confidenceValue = document.getElementById('confidenceValue');
const linesList = document.getElementById('linesList');
const statusDot = document.getElementById('statusDot');
const statusText = document.getElementById('statusText');
async function checkStatus() {
try {
const r = await fetch(`${API_URL}/health`);
if (r.ok) {
statusDot.classList.add('online');
statusText.textContent = 'Servidor activo';
}
} catch (e) {
statusDot.classList.remove('online');
statusText.textContent = 'Desconectado';
}
}
checkStatus();
setInterval(checkStatus, 5000);
selectBtn.onclick = (e) => { e.stopPropagation(); fileInput.click(); };
uploadArea.onclick = () => fileInput.click();
uploadArea.ondragover = (e) => { e.preventDefault(); uploadArea.style.borderColor = '#00ff88'; };
uploadArea.ondragleave = () => { uploadArea.style.borderColor = 'rgba(255,255,255,0.2)'; };
uploadArea.ondrop = (e) => {
e.preventDefault();
uploadArea.style.borderColor = 'rgba(255,255,255,0.2)';
if (e.dataTransfer.files[0]) processFile(e.dataTransfer.files[0]);
};
fileInput.onchange = (e) => { if (e.target.files[0]) processFile(e.target.files[0]); };
async function processFile(file) {
const reader = new FileReader();
reader.onload = (e) => { previewImg.src = e.target.result; };
reader.readAsDataURL(file);
loading.classList.add('show');
error.classList.remove('show');
previewSection.classList.remove('show');
try {
const formData = new FormData();
formData.append('file', file);
const response = await fetch(`${API_URL}/recognize`, {
method: 'POST',
body: formData
});
if (!response.ok) throw new Error((await response.json()).detail || 'Error');
const data = await response.json();
loading.classList.remove('show');
previewSection.classList.add('show');
resultText.textContent = data.texto || 'No se detectó texto';
const conf = Math.round(data.confianza * 100);
confidenceBar.style.width = conf + '%';
confidenceValue.textContent = conf + '%';
linesList.innerHTML = '';
if (data.lineas) {
data.lineas.forEach(line => {
const div = document.createElement('div');
div.className = 'line-item';
div.innerHTML = `<span>${line.texto}</span><span class="line-conf">${Math.round(line.confianza*100)}%</span>`;
linesList.appendChild(div);
});
}
} catch (e) {
loading.classList.remove('show');
error.classList.add('show');
errorText.textContent = e.message;
}
}
</script>
</body>
</html>