ocr / templates /index.html
jeyanthangj2004's picture
Upload 110 files
3f42a6f verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>eDOCr2 - Engineering Drawing OCR</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.header {
text-align: center;
color: white;
margin-bottom: 40px;
}
.header h1 {
font-size: 3em;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.header p {
font-size: 1.2em;
opacity: 0.9;
}
.card {
background: white;
border-radius: 20px;
padding: 40px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
margin-bottom: 30px;
}
.upload-area {
border: 3px dashed #667eea;
border-radius: 15px;
padding: 60px 20px;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
background: #f8f9ff;
}
.upload-area:hover {
border-color: #764ba2;
background: #f0f2ff;
transform: translateY(-2px);
}
.upload-area.dragover {
border-color: #764ba2;
background: #e8ebff;
}
.upload-icon {
font-size: 4em;
margin-bottom: 20px;
}
.upload-text {
font-size: 1.3em;
color: #667eea;
margin-bottom: 10px;
font-weight: 600;
}
.upload-hint {
color: #666;
font-size: 0.9em;
}
#fileInput {
display: none;
}
.btn {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 15px 40px;
border-radius: 30px;
font-size: 1.1em;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
font-weight: 600;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
.processing {
display: none;
text-align: center;
padding: 40px;
}
.spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #667eea;
border-radius: 50%;
width: 60px;
height: 60px;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.results {
display: none;
}
.result-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 2px solid #eee;
}
.result-header h2 {
color: #333;
font-size: 2em;
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 25px;
border-radius: 15px;
text-align: center;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}
.stat-value {
font-size: 2.5em;
font-weight: bold;
margin-bottom: 5px;
}
.stat-label {
font-size: 0.9em;
opacity: 0.9;
}
.mask-image {
width: 100%;
border-radius: 15px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
margin-bottom: 30px;
}
.data-section {
margin-top: 30px;
}
.data-section h3 {
color: #667eea;
margin-bottom: 15px;
font-size: 1.5em;
}
.data-content {
background: #f8f9ff;
padding: 20px;
border-radius: 10px;
max-height: 400px;
overflow-y: auto;
font-family: 'Courier New', monospace;
font-size: 0.9em;
}
.error {
background: #fee;
color: #c33;
padding: 20px;
border-radius: 10px;
border-left: 4px solid #c33;
margin-top: 20px;
}
.success {
background: #efe;
color: #3c3;
padding: 20px;
border-radius: 10px;
border-left: 4px solid #3c3;
margin-top: 20px;
}
.btn-group {
display: flex;
gap: 15px;
margin-top: 20px;
}
.btn-secondary {
background: #6c757d;
}
.btn-secondary:hover {
background: #5a6268;
}
@media (max-width: 768px) {
.header h1 {
font-size: 2em;
}
.card {
padding: 20px;
}
.stats {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🔧 eDOCr2</h1>
<p>Engineering Drawing OCR - Extract dimensions, tables, and GD&T symbols</p>
</div>
<div class="card">
<div id="uploadSection">
<div class="upload-area" id="uploadArea">
<div class="upload-icon">📄</div>
<div class="upload-text">Drop your drawing here or click to browse</div>
<div class="upload-hint">Supported formats: JPG, PNG, PDF (Max 50MB)</div>
</div>
<input type="file" id="fileInput" accept=".jpg,.jpeg,.png,.pdf">
</div>
<div class="processing" id="processingSection">
<div class="spinner"></div>
<h3>Processing your drawing...</h3>
<p>This may take 10-30 seconds depending on complexity</p>
</div>
<div class="results" id="resultsSection">
<div class="result-header">
<h2>📊 Results</h2>
<div class="btn-group">
<button class="btn" onclick="downloadResults()">⬇️ Download All</button>
<button class="btn btn-secondary" onclick="resetApp()">🔄 Process Another</button>
</div>
</div>
<div class="stats" id="statsSection"></div>
<h3 style="color: #667eea; margin-bottom: 15px;">🎨 Annotated Drawing</h3>
<img id="maskImage" class="mask-image" alt="Processed drawing">
<div class="data-section">
<h3>📋 Extracted Data</h3>
<div class="data-content" id="dataContent"></div>
</div>
</div>
<div id="messageArea"></div>
</div>
</div>
<script>
const uploadArea = document.getElementById('uploadArea');
const fileInput = document.getElementById('fileInput');
const uploadSection = document.getElementById('uploadSection');
const processingSection = document.getElementById('processingSection');
const resultsSection = document.getElementById('resultsSection');
const messageArea = document.getElementById('messageArea');
let currentResultFolder = null;
// Click to upload
uploadArea.addEventListener('click', () => fileInput.click());
// Drag and drop
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('dragover');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('dragover');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('dragover');
const files = e.dataTransfer.files;
if (files.length > 0) {
handleFile(files[0]);
}
});
// File input change
fileInput.addEventListener('change', (e) => {
if (e.target.files.length > 0) {
handleFile(e.target.files[0]);
}
});
function handleFile(file) {
// Validate file
const validTypes = ['image/jpeg', 'image/png', 'application/pdf'];
if (!validTypes.includes(file.type)) {
showMessage('Invalid file type. Please upload JPG, PNG, or PDF.', 'error');
return;
}
if (file.size > 50 * 1024 * 1024) {
showMessage('File too large. Maximum size is 50MB.', 'error');
return;
}
// Upload and process
uploadFile(file);
}
async function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
// Show processing
uploadSection.style.display = 'none';
processingSection.style.display = 'block';
messageArea.innerHTML = '';
try {
const response = await fetch('/upload', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
displayResults(result);
} else {
showMessage(`Error: ${result.error}`, 'error');
resetApp();
}
} catch (error) {
showMessage(`Upload failed: ${error.message}`, 'error');
resetApp();
}
}
function displayResults(result) {
processingSection.style.display = 'none';
resultsSection.style.display = 'block';
currentResultFolder = result.output_dir;
// Display stats
const statsHTML = `
<div class="stat-card">
<div class="stat-value">${result.stats.tables_found}</div>
<div class="stat-label">Tables Found</div>
</div>
<div class="stat-card">
<div class="stat-value">${result.stats.gdt_symbols}</div>
<div class="stat-label">GD&T Symbols</div>
</div>
<div class="stat-card">
<div class="stat-value">${result.stats.dimensions}</div>
<div class="stat-label">Dimensions</div>
</div>
<div class="stat-card">
<div class="stat-value">${result.processing_time}s</div>
<div class="stat-label">Processing Time</div>
</div>
`;
document.getElementById('statsSection').innerHTML = statsHTML;
// Display mask image
document.getElementById('maskImage').src = `/results/${result.output_dir}/${result.mask_path}`;
// Display extracted data
const dataHTML = `<pre>${JSON.stringify(result.data, null, 2)}</pre>`;
document.getElementById('dataContent').innerHTML = dataHTML;
showMessage('Processing completed successfully!', 'success');
}
function downloadResults() {
if (currentResultFolder) {
window.location.href = `/download/${currentResultFolder}`;
}
}
function resetApp() {
uploadSection.style.display = 'block';
processingSection.style.display = 'none';
resultsSection.style.display = 'none';
messageArea.innerHTML = '';
fileInput.value = '';
currentResultFolder = null;
}
function showMessage(message, type) {
const className = type === 'error' ? 'error' : 'success';
messageArea.innerHTML = `<div class="${className}">${message}</div>`;
}
// Check server health on load
fetch('/health')
.then(r => r.json())
.then(data => {
if (!data.models_loaded) {
showMessage('Warning: Models not loaded. Please check server logs.', 'error');
}
})
.catch(err => {
showMessage('Cannot connect to server.', 'error');
});
</script>
</body>
</html>