| 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('-') |
| |
| |
| 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', |
| 'b64_image': get_base64_image(filepath) |
| }) |
|
|
| |
| objects.sort(key=lambda x: (x['class'], x['id'])) |
| |
| total_objects = len(objects) |
| damaged_objects = 0 |
| |
| |
| 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() |
|
|