Ai_Inspection_System / generate_html_report.py
RehamAAhmed's picture
Upload 18 files
a0554a9 verified
Raw
History Blame Contribute Delete
7.39 kB
import os
import glob
import base64
from collections import defaultdict
def get_base64_image(image_path):
with open(image_path, "rb") as image_file:
encoded_string = base64.b64encode(image_file.read()).decode('utf-8')
ext = os.path.splitext(image_path)[1].lower().replace('.', '')
if ext == 'jpg': ext = 'jpeg'
return f"data:image/{ext};base64,{encoded_string}"
def generate_report():
directory = r"E:\University\LEVEL 4\Graduation Project\Damage Detection\small_Mod\Apartments\Apartment_01\Before\segmented_objects_b"
image_files = glob.glob(os.path.join(directory, "*.*"))
image_files = [f for f in image_files if f.lower().endswith(('.jpg', '.png', '.jpeg')) and not f.endswith('collage.jpg')]
objects = []
class_counts = defaultdict(int)
for filepath in image_files:
filename = os.path.basename(filepath)
name_parts = os.path.splitext(filename)[0].split('-')
# Parse class and ID
if len(name_parts) >= 2:
obj_class = name_parts[0].capitalize().replace('_', ' ')
obj_id = name_parts[1]
else:
obj_class = "Unknown"
obj_id = "N/A"
class_counts[obj_class] += 1
objects.append({
'class': obj_class,
'id': obj_id,
'filename': filename,
'path': filepath,
'status': 'Undamaged', # As verified previously
'b64_image': get_base64_image(filepath)
})
# Sort objects by class then ID
objects.sort(key=lambda x: (x['class'], x['id']))
total_objects = len(objects)
damaged_objects = 0
# HTML Template
html_content = f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Apartment 01 - Damage Detection Report</title>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&display=swap" rel="stylesheet">
<style>
:root {{
--bg-color: #f4f7f6;
--text-main: #2c3e50;
--accent: #3498db;
--card-bg: #ffffff;
--success: #2ecc71;
--danger: #e74c3c;
}}
body {{
font-family: 'Outfit', sans-serif;
background-color: var(--bg-color);
color: var(--text-main);
margin: 0;
padding: 40px;
}}
.header {{
text-align: center;
margin-bottom: 40px;
padding: 30px;
background: linear-gradient(135deg, #2c3e50, #3498db);
color: white;
border-radius: 16px;
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
}}
.header h1 {{
margin: 0;
font-size: 2.5rem;
letter-spacing: 1px;
}}
.header p {{
margin-top: 10px;
font-size: 1.2rem;
opacity: 0.9;
}}
.summary-cards {{
display: flex;
gap: 20px;
margin-bottom: 40px;
justify-content: center;
}}
.stat-card {{
background: var(--card-bg);
padding: 20px 40px;
border-radius: 12px;
text-align: center;
box-shadow: 0 4px 6px rgba(0,0,0,0.05);
flex: 1;
max-width: 250px;
}}
.stat-card h3 {{
margin: 0;
font-size: 1.1rem;
color: #7f8c8d;
}}
.stat-card .value {{
font-size: 2.5rem;
font-weight: 800;
color: var(--text-main);
margin-top: 10px;
}}
.stat-card.success .value {{ color: var(--success); }}
.stat-card.danger .value {{ color: var(--danger); }}
.grid {{
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 25px;
}}
.card {{
background: var(--card-bg);
border-radius: 12px;
overflow: hidden;
box-shadow: 0 6px 12px rgba(0,0,0,0.06);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}}
.card:hover {{
transform: translateY(-5px);
box-shadow: 0 12px 24px rgba(0,0,0,0.12);
}}
.card-img-container {{
width: 100%;
height: 180px;
background-color: #ecf0f1;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}}
.card img {{
max-width: 100%;
max-height: 100%;
object-fit: contain;
}}
.card-body {{
padding: 15px;
}}
.class-title {{
font-size: 1.2rem;
font-weight: 600;
margin: 0 0 5px 0;
}}
.object-id {{
color: #7f8c8d;
font-size: 0.9rem;
margin-bottom: 15px;
}}
.badge {{
display: inline-block;
padding: 5px 10px;
border-radius: 20px;
font-size: 0.8rem;
font-weight: 600;
background-color: rgba(46, 204, 113, 0.1);
color: var(--success);
}}
.badge.damaged {{
background-color: rgba(231, 76, 60, 0.1);
color: var(--danger);
}}
.section-title {{
margin: 40px 0 20px 0;
font-size: 1.8rem;
border-bottom: 2px solid #ecf0f1;
padding-bottom: 10px;
}}
</style>
</head>
<body>
<div class="header">
<h1>Damage Detection Report</h1>
<p>Apartment 01 - "Before" State Analysis</p>
</div>
<div class="summary-cards">
<div class="stat-card">
<h3>Total Objects</h3>
<div class="value">{total_objects}</div>
</div>
<div class="stat-card success">
<h3>Intact / Undamaged</h3>
<div class="value">{total_objects - damaged_objects}</div>
</div>
<div class="stat-card danger">
<h3>Damaged</h3>
<div class="value">{damaged_objects}</div>
</div>
</div>
<h2 class="section-title">Object Inventory & Status</h2>
<div class="grid">
"""
for obj in objects:
badge_class = "badge damaged" if obj['status'] == 'Damaged' else "badge"
html_content += f"""
<div class="card">
<div class="card-img-container">
<img src="{obj['b64_image']}" alt="{obj['class']} {obj['id']}">
</div>
<div class="card-body">
<h4 class="class-title">{obj['class']}</h4>
<div class="object-id">ID: #{obj['id']}</div>
<span class="{badge_class}">{obj['status']}</span>
</div>
</div>
"""
html_content += """
</div>
</body>
</html>
"""
output_path = r"E:\University\LEVEL 4\Graduation Project\Damage Detection\small_Mod\Apartments\Apartment_01\Before\Damage_Report.html"
with open(output_path, "w", encoding="utf-8") as f:
f.write(html_content)
print(f"HTML Report successfully generated at: {output_path}")
if __name__ == "__main__":
generate_report()