|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Advanced AI Property Image Analyzer</title> |
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"> |
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet"> |
|
<style> |
|
body { |
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
min-height: 100vh; |
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
|
} |
|
.main-container { |
|
background: rgba(255, 255, 255, 0.95); |
|
border-radius: 20px; |
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1); |
|
backdrop-filter: blur(10px); |
|
margin: 20px auto; |
|
max-width: 1400px; |
|
} |
|
.header { |
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
color: white; |
|
padding: 30px; |
|
border-radius: 20px 20px 0 0; |
|
text-align: center; |
|
} |
|
.upload-area { |
|
border: 3px dashed #667eea; |
|
border-radius: 15px; |
|
padding: 40px; |
|
text-align: center; |
|
background: rgba(102, 126, 234, 0.05); |
|
transition: all 0.3s ease; |
|
cursor: pointer; |
|
} |
|
.upload-area:hover { |
|
border-color: #764ba2; |
|
background: rgba(118, 75, 162, 0.05); |
|
} |
|
.upload-area.dragover { |
|
border-color: #764ba2; |
|
background: rgba(118, 75, 162, 0.1); |
|
} |
|
.result-card { |
|
background: white; |
|
border-radius: 15px; |
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); |
|
margin: 20px 0; |
|
overflow: hidden; |
|
transition: transform 0.3s ease; |
|
} |
|
.result-card:hover { |
|
transform: translateY(-5px); |
|
} |
|
.result-header { |
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
color: white; |
|
padding: 20px; |
|
font-weight: bold; |
|
} |
|
.result-body { |
|
padding: 25px; |
|
} |
|
.metric-card { |
|
background: #f8f9fa; |
|
border-radius: 10px; |
|
padding: 20px; |
|
margin: 10px 0; |
|
border-left: 4px solid #667eea; |
|
} |
|
.score-badge { |
|
font-size: 1.2em; |
|
font-weight: bold; |
|
padding: 8px 16px; |
|
border-radius: 20px; |
|
} |
|
.score-excellent { background: #28a745; color: white; } |
|
.score-good { background: #17a2b8; color: white; } |
|
.score-fair { background: #ffc107; color: black; } |
|
.score-poor { background: #dc3545; color: white; } |
|
.ai-model-badge { |
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
color: white; |
|
padding: 5px 12px; |
|
border-radius: 15px; |
|
font-size: 0.8em; |
|
margin: 2px; |
|
display: inline-block; |
|
} |
|
.loading { |
|
text-align: center; |
|
padding: 40px; |
|
} |
|
.spinner { |
|
border: 4px solid #f3f3f3; |
|
border-top: 4px solid #667eea; |
|
border-radius: 50%; |
|
width: 50px; |
|
height: 50px; |
|
animation: spin 1s linear infinite; |
|
margin: 0 auto 20px; |
|
} |
|
@keyframes spin { |
|
0% { transform: rotate(0deg); } |
|
100% { transform: rotate(360deg); } |
|
} |
|
.progress-bar { |
|
height: 8px; |
|
border-radius: 4px; |
|
background: #e9ecef; |
|
overflow: hidden; |
|
} |
|
.progress-fill { |
|
height: 100%; |
|
background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); |
|
transition: width 0.3s ease; |
|
} |
|
.insight-card { |
|
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); |
|
border-radius: 10px; |
|
padding: 15px; |
|
margin: 10px 0; |
|
border-left: 4px solid #28a745; |
|
} |
|
.warning-card { |
|
background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%); |
|
border-radius: 10px; |
|
padding: 15px; |
|
margin: 10px 0; |
|
border-left: 4px solid #ffc107; |
|
} |
|
.error-card { |
|
background: linear-gradient(135deg, #f8d7da 0%, #f5c6cb 100%); |
|
border-radius: 10px; |
|
padding: 15px; |
|
margin: 10px 0; |
|
border-left: 4px solid #dc3545; |
|
} |
|
.object-item { |
|
background: white; |
|
border: 1px solid #dee2e6; |
|
border-radius: 8px; |
|
padding: 10px; |
|
margin: 5px 0; |
|
display: flex; |
|
justify-content: space-between; |
|
align-items: center; |
|
} |
|
.confidence-bar { |
|
width: 100px; |
|
height: 6px; |
|
background: #e9ecef; |
|
border-radius: 3px; |
|
overflow: hidden; |
|
} |
|
.confidence-fill { |
|
height: 100%; |
|
background: linear-gradient(90deg, #28a745 0%, #17a2b8 100%); |
|
transition: width 0.3s ease; |
|
} |
|
.scene-concept { |
|
background: rgba(102, 126, 234, 0.1); |
|
border-radius: 8px; |
|
padding: 8px 12px; |
|
margin: 5px; |
|
display: inline-block; |
|
font-size: 0.9em; |
|
} |
|
.market-tier-badge { |
|
font-size: 1.1em; |
|
font-weight: bold; |
|
padding: 10px 20px; |
|
border-radius: 25px; |
|
text-transform: uppercase; |
|
letter-spacing: 1px; |
|
} |
|
.tier-luxury { background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%); color: #333; } |
|
.tier-premium { background: linear-gradient(135deg, #c0c0c0 0%, #e5e5e5 100%); color: #333; } |
|
.tier-standard { background: linear-gradient(135deg, #cd7f32 0%, #daa520 100%); color: white; } |
|
.tier-value { background: linear-gradient(135deg, #6c757d 0%, #495057 100%); color: white; } |
|
</style> |
|
</head> |
|
<body> |
|
<div class="container-fluid"> |
|
<div class="main-container"> |
|
<div class="header"> |
|
<h1><i class="fas fa-robot"></i> Advanced AI Property Image Analyzer</h1> |
|
<p class="mb-0">State-of-the-art AI-powered real estate image analysis with comprehensive insights</p> |
|
</div> |
|
|
|
<div class="container-fluid p-4"> |
|
<div class="row"> |
|
<div class="col-md-6"> |
|
<div class="upload-area" id="uploadArea"> |
|
<i class="fas fa-cloud-upload-alt fa-3x text-primary mb-3"></i> |
|
<h4>Upload Property Image</h4> |
|
<p>Drag and drop your image here or click to browse</p> |
|
<input type="file" id="imageInput" accept="image/*" style="display: none;"> |
|
<button class="btn btn-primary btn-lg" onclick="document.getElementById('imageInput').click()"> |
|
<i class="fas fa-upload"></i> Choose Image |
|
</button> |
|
</div> |
|
|
|
<div id="imagePreview" class="mt-3" style="display: none;"> |
|
<img id="previewImg" class="img-fluid rounded" style="max-height: 300px;"> |
|
</div> |
|
</div> |
|
|
|
<div class="col-md-6"> |
|
<div id="loading" class="loading" style="display: none;"> |
|
<div class="spinner"></div> |
|
<h4>Advanced AI Analysis in Progress...</h4> |
|
<p>Multiple AI models are analyzing your image</p> |
|
<div class="progress-bar"> |
|
<div class="progress-fill" style="width: 0%"></div> |
|
</div> |
|
</div> |
|
|
|
<div id="results" style="display: none;"> |
|
|
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script> |
|
<script> |
|
const uploadArea = document.getElementById('uploadArea'); |
|
const imageInput = document.getElementById('imageInput'); |
|
const imagePreview = document.getElementById('imagePreview'); |
|
const previewImg = document.getElementById('previewImg'); |
|
const loading = document.getElementById('loading'); |
|
const results = document.getElementById('results'); |
|
|
|
// Drag and drop functionality |
|
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) { |
|
handleImageUpload(files[0]); |
|
} |
|
}); |
|
|
|
uploadArea.addEventListener('click', () => { |
|
imageInput.click(); |
|
}); |
|
|
|
imageInput.addEventListener('change', (e) => { |
|
if (e.target.files.length > 0) { |
|
handleImageUpload(e.target.files[0]); |
|
} |
|
}); |
|
|
|
function handleImageUpload(file) { |
|
if (!file.type.startsWith('image/')) { |
|
alert('Please select a valid image file.'); |
|
return; |
|
} |
|
|
|
// Show preview |
|
const reader = new FileReader(); |
|
reader.onload = (e) => { |
|
previewImg.src = e.target.result; |
|
imagePreview.style.display = 'block'; |
|
}; |
|
reader.readAsDataURL(file); |
|
|
|
// Start analysis |
|
analyzeImage(file); |
|
} |
|
|
|
function analyzeImage(file) { |
|
const formData = new FormData(); |
|
formData.append('image', file); |
|
|
|
// Show loading |
|
loading.style.display = 'block'; |
|
results.style.display = 'none'; |
|
|
|
// Simulate progress |
|
let progress = 0; |
|
const progressFill = document.querySelector('.progress-fill'); |
|
const progressInterval = setInterval(() => { |
|
progress += Math.random() * 15; |
|
if (progress > 90) progress = 90; |
|
progressFill.style.width = progress + '%'; |
|
}, 500); |
|
|
|
fetch('/analyze', { |
|
method: 'POST', |
|
body: formData |
|
}) |
|
.then(response => response.json()) |
|
.then(data => { |
|
clearInterval(progressInterval); |
|
progressFill.style.width = '100%'; |
|
|
|
setTimeout(() => { |
|
loading.style.display = 'none'; |
|
displayResults(data); |
|
}, 1000); |
|
}) |
|
.catch(error => { |
|
clearInterval(progressInterval); |
|
loading.style.display = 'none'; |
|
alert('Analysis failed: ' + error.message); |
|
}); |
|
} |
|
|
|
function displayResults(data) { |
|
if (data.error) { |
|
results.innerHTML = ` |
|
<div class="error-card"> |
|
<h5><i class="fas fa-exclamation-triangle"></i> Analysis Error</h5> |
|
<p>${data.error}</p> |
|
</div> |
|
`; |
|
results.style.display = 'block'; |
|
return; |
|
} |
|
|
|
const summary = data.analysis_summary; |
|
const roomClass = data.room_classification; |
|
const quality = data.quality_analysis; |
|
const objects = data.object_detection; |
|
const assessment = data.property_assessment; |
|
const insights = data.property_insights; |
|
const scene = data.scene_analysis; |
|
const market = data.market_analysis; |
|
const roomSize = data.room_size_estimation; |
|
|
|
results.innerHTML = ` |
|
|
|
<div class="result-card"> |
|
<div class="result-header"> |
|
<h4><i class="fas fa-chart-line"></i> Analysis Summary</h4> |
|
</div> |
|
<div class="result-body"> |
|
<div class="row"> |
|
<div class="col-md-6"> |
|
<div class="metric-card"> |
|
<h6><i class="fas fa-home"></i> Room Type</h6> |
|
<p class="mb-1"><strong>${roomClass.room_type}</strong></p> |
|
<div class="confidence-bar"> |
|
<div class="confidence-fill" style="width: ${roomClass.confidence}%"></div> |
|
</div> |
|
<small>Confidence: ${roomClass.confidence}%</small> |
|
</div> |
|
</div> |
|
<div class="col-md-6"> |
|
<div class="metric-card"> |
|
<h6><i class="fas fa-star"></i> Overall Score</h6> |
|
<span class="score-badge ${getScoreClass(assessment.overall_score)}">${assessment.overall_score}/100</span> |
|
<p class="mt-2 mb-0"><small>${assessment.professional_grade ? 'Professional Grade' : 'Standard Grade'}</small></p> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="row mt-3"> |
|
<div class="col-md-4"> |
|
<div class="metric-card"> |
|
<h6><i class="fas fa-camera"></i> Quality Score</h6> |
|
<span class="score-badge ${getScoreClass(quality.quality_score)}">${quality.quality_score}/100</span> |
|
<p class="mt-2 mb-0"><small>${quality.quality_level}</small></p> |
|
</div> |
|
</div> |
|
<div class="col-md-4"> |
|
<div class="metric-card"> |
|
<h6><i class="fas fa-cube"></i> Objects Detected</h6> |
|
<span class="score-badge score-good">${objects.objects.length}</span> |
|
<p class="mt-2 mb-0"><small>${objects.analysis.total_objects || 0} total</small></p> |
|
</div> |
|
</div> |
|
<div class="col-md-4"> |
|
<div class="metric-card"> |
|
<h6><i class="fas fa-ruler-combined"></i> Room Size</h6> |
|
<span class="score-badge score-good">${roomSize.estimated_size}</span> |
|
<p class="mt-2 mb-0"><small>Confidence: ${roomSize.confidence}</small></p> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="result-card"> |
|
<div class="result-header"> |
|
<h4><i class="fas fa-chart-bar"></i> Market Analysis</h4> |
|
</div> |
|
<div class="result-body"> |
|
<div class="row"> |
|
<div class="col-md-6"> |
|
<div class="market-tier-badge tier-${market.market_tier.toLowerCase()}">${market.market_tier}</div> |
|
<p class="mt-2"><strong>Target Market:</strong> ${market.target_market}</p> |
|
<p><strong>Price Positioning:</strong> ${market.price_positioning}</p> |
|
<p><strong>Competitive Position:</strong> ${market.competitive_position}</p> |
|
</div> |
|
<div class="col-md-6"> |
|
<h6>Competitive Advantages:</h6> |
|
<ul> |
|
${market.competitive_advantages.map(adv => `<li>${adv}</li>`).join('')} |
|
</ul> |
|
|
|
<h6>Differentiation Factors:</h6> |
|
<ul> |
|
${market.differentiation_factors.map(factor => `<li>${factor}</li>`).join('')} |
|
</ul> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="result-card"> |
|
<div class="result-header"> |
|
<h4><i class="fas fa-lightbulb"></i> Property Insights</h4> |
|
</div> |
|
<div class="result-body"> |
|
<div class="row"> |
|
<div class="col-md-6"> |
|
<h6><i class="fas fa-bullhorn"></i> Marketing Tips</h6> |
|
<ul> |
|
${insights.marketing_tips.map(tip => `<li>${tip}</li>`).join('')} |
|
</ul> |
|
|
|
<h6><i class="fas fa-users"></i> Target Audience</h6> |
|
<ul> |
|
${insights.target_audience.map(audience => `<li>${audience}</li>`).join('')} |
|
</ul> |
|
</div> |
|
<div class="col-md-6"> |
|
<h6><i class="fas fa-dollar-sign"></i> Pricing Considerations</h6> |
|
<ul> |
|
${insights.pricing_considerations.map(consideration => `<li>${consideration}</li>`).join('')} |
|
</ul> |
|
|
|
<h6><i class="fas fa-tools"></i> Improvement Suggestions</h6> |
|
<ul> |
|
${insights.improvement_suggestions.map(suggestion => `<li>${suggestion}</li>`).join('')} |
|
</ul> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="result-card"> |
|
<div class="result-header"> |
|
<h4><i class="fas fa-palette"></i> Scene Analysis</h4> |
|
</div> |
|
<div class="result-body"> |
|
<div class="row"> |
|
<div class="col-md-6"> |
|
<h6>Dominant Style: ${scene.style_analysis.dominant_style}</h6> |
|
<p><strong>Style Confidence:</strong> ${(scene.style_analysis.style_confidence * 100).toFixed(1)}%</p> |
|
|
|
<h6>Scene Concepts:</h6> |
|
<div> |
|
${scene.scene_concepts.map(concept => |
|
`<span class="scene-concept">${concept.concept} (${(concept.confidence * 100).toFixed(1)}%)</span>` |
|
).join('')} |
|
</div> |
|
</div> |
|
<div class="col-md-6"> |
|
<h6>Quality Indicators:</h6> |
|
<ul> |
|
${scene.style_analysis.quality_indicators.map(indicator => |
|
`<li>${indicator.concept} (${(indicator.confidence * 100).toFixed(1)}%)</li>` |
|
).join('')} |
|
</ul> |
|
|
|
<h6>Functionality Indicators:</h6> |
|
<ul> |
|
${scene.style_analysis.functionality_indicators.map(indicator => |
|
`<li>${indicator.concept} (${(indicator.confidence * 100).toFixed(1)}%)</li>` |
|
).join('')} |
|
</ul> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="result-card"> |
|
<div class="result-header"> |
|
<h4><i class="fas fa-microscope"></i> Detailed Analysis</h4> |
|
</div> |
|
<div class="result-body"> |
|
<div class="row"> |
|
<div class="col-md-6"> |
|
<h6>Strengths:</h6> |
|
<ul> |
|
${assessment.strengths.map(strength => `<li class="insight-card">${strength}</li>`).join('')} |
|
</ul> |
|
|
|
<h6>Weaknesses:</h6> |
|
<ul> |
|
${assessment.weaknesses.map(weakness => `<li class="warning-card">${weakness}</li>`).join('')} |
|
</ul> |
|
</div> |
|
<div class="col-md-6"> |
|
<h6>Recommendations:</h6> |
|
<ul> |
|
${assessment.recommendations.map(rec => `<li class="insight-card">${rec}</li>`).join('')} |
|
</ul> |
|
|
|
<h6>Market Opportunities:</h6> |
|
<ul> |
|
${market.market_opportunities.map(opp => `<li class="insight-card">${opp}</li>`).join('')} |
|
</ul> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="result-card"> |
|
<div class="result-header"> |
|
<h4><i class="fas fa-cube"></i> Object Detection</h4> |
|
</div> |
|
<div class="result-body"> |
|
<div class="row"> |
|
<div class="col-md-6"> |
|
<h6>Detected Objects:</h6> |
|
${objects.objects.map(obj => ` |
|
<div class="object-item"> |
|
<span><i class="fas fa-tag"></i> ${obj.label}</span> |
|
<div class="confidence-bar"> |
|
<div class="confidence-fill" style="width: ${obj.confidence * 100}%"></div> |
|
</div> |
|
<small>${(obj.confidence * 100).toFixed(1)}%</small> |
|
</div> |
|
`).join('')} |
|
</div> |
|
<div class="col-md-6"> |
|
<h6>Object Categories:</h6> |
|
<p><strong>Furniture:</strong> ${objects.analysis.object_categories?.furniture?.length || 0} items</p> |
|
<p><strong>Appliances:</strong> ${objects.analysis.object_categories?.appliances?.length || 0} items</p> |
|
<p><strong>Fixtures:</strong> ${objects.analysis.object_categories?.fixtures?.length || 0} items</p> |
|
<p><strong>Other:</strong> ${objects.analysis.object_categories?.other?.length || 0} items</p> |
|
|
|
<h6>Composition Analysis:</h6> |
|
<p><strong>Object Density:</strong> ${objects.analysis.object_density?.toFixed(2) || 0}</p> |
|
<p><strong>Composition Balance:</strong> ${(objects.analysis.composition_balance * 100).toFixed(1)}%</p> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="result-card"> |
|
<div class="result-header"> |
|
<h4><i class="fas fa-robot"></i> AI Models Used</h4> |
|
</div> |
|
<div class="result-body"> |
|
<div> |
|
<span class="ai-model-badge">Room Classification</span> |
|
<span class="ai-model-badge">BLIP Captioning</span> |
|
<span class="ai-model-badge">DETR Detection</span> |
|
<span class="ai-model-badge">Quality Analysis</span> |
|
<span class="ai-model-badge">CLIP Scene Understanding</span> |
|
<span class="ai-model-badge">Market Analysis</span> |
|
</div> |
|
<p class="mt-3 mb-0"><small><i class="fas fa-clock"></i> Analysis completed at ${new Date().toLocaleString()}</small></p> |
|
</div> |
|
</div> |
|
`; |
|
|
|
results.style.display = 'block'; |
|
} |
|
|
|
function getScoreClass(score) { |
|
if (score >= 85) return 'score-excellent'; |
|
if (score >= 70) return 'score-good'; |
|
if (score >= 50) return 'score-fair'; |
|
return 'score-poor'; |
|
} |
|
</script> |
|
</body> |
|
</html> |