{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 04 — Sestavení velké mapy\n", "\n", "Složíme otagované dlaždice (z notebooku 03) do jednoho velkého leteckého snímku HK\n", "se zakreslenými detekcemi vozidel.\n", "\n", "Výsledná mapa: **74 × 69 dlaždic × 256 px = 18 944 × 17 664 px**\n", "(přibližně 90 MB PNG nebo 15 MB JPEG)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from pathlib import Path\n", "from PIL import Image\n", "import numpy as np\n", "import re\n", "from tqdm.notebook import tqdm\n", "import json\n", "\n", "ANNOTATED_DIR = Path(\"tiles_annotated\")\n", "assert ANNOTATED_DIR.exists(), \"Nejprve spusť 03_inference_tiles.ipynb!\"\n", "\n", "TILE_SIZE = 256\n", "\n", "# Zjistíme rozsah mřížky\n", "tile_files = sorted(ANNOTATED_DIR.glob(\"18_*.jpg\"))\n", "xs = []; ys = []\n", "for f in tile_files:\n", " m = re.match(r\"18_(\\d+)_(\\d+)\\.jpg\", f.name)\n", " if m:\n", " xs.append(int(m.group(1)))\n", " ys.append(int(m.group(2)))\n", "\n", "x_min, x_max = min(xs), max(xs)\n", "y_min, y_max = min(ys), max(ys)\n", "cols = x_max - x_min + 1\n", "rows = y_max - y_min + 1\n", "\n", "W = cols * TILE_SIZE\n", "H = rows * TILE_SIZE\n", "\n", "print(f\"Mřížka: {cols} × {rows} dlaždic\")\n", "print(f\"Výsledná mapa: {W} × {H} px\")\n", "print(f\"Odhadovaná velikost (JPEG 85%): ~{W*H*3/1e6/10:.0f} MB\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Sestavení mapy — plná rozlišení\n", "# Poznámka: ~18k × 17k px = ~3 GB v RAM → použijeme chunky nebo zmenšenou verzi\n", "\n", "OUTPUT_FULL = Path(\"hk_annotated_full.jpg\")\n", "OUTPUT_PREVIEW = Path(\"hk_annotated_preview.jpg\")\n", "PREVIEW_SCALE = 4 # 1/4 rozlišení pro preview\n", "\n", "# Zkontroluj dostupnou RAM\n", "import psutil\n", "ram_gb = psutil.virtual_memory().available / 1e9\n", "needed_gb = W * H * 3 / 1e9\n", "print(f\"Dostupná RAM: {ram_gb:.1f} GB, potřeba pro plnou mapu: {needed_gb:.1f} GB\")\n", "\n", "if needed_gb > ram_gb * 0.8:\n", " print(\"⚠ Nedostatek RAM pro plnou mapu — generuji pouze preview\")\n", " BUILD_FULL = False\n", "else:\n", " print(\"RAM dostačuje — generuji plnou mapu\")\n", " BUILD_FULL = True" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Sestavení preview mapy (vždy)\n", "pw = W // PREVIEW_SCALE\n", "ph = H // PREVIEW_SCALE\n", "ts_small = TILE_SIZE // PREVIEW_SCALE\n", "\n", "canvas_preview = Image.new(\"RGB\", (pw, ph), (30, 30, 30))\n", "\n", "missing = 0\n", "for f in tqdm(tile_files, desc=\"Preview\"):\n", " m = re.match(r\"18_(\\d+)_(\\d+)\\.jpg\", f.name)\n", " if not m:\n", " continue\n", " xi = int(m.group(1)) - x_min\n", " yi = int(m.group(2)) - y_min\n", " try:\n", " tile = Image.open(f).resize((ts_small, ts_small), Image.LANCZOS)\n", " canvas_preview.paste(tile, (xi * ts_small, yi * ts_small))\n", " except Exception as e:\n", " missing += 1\n", "\n", "canvas_preview.save(OUTPUT_PREVIEW, quality=88, optimize=True)\n", "print(f\"Preview mapa: {OUTPUT_PREVIEW} ({pw}×{ph} px)\")\n", "if missing:\n", " print(f\" Chybějící dlaždice: {missing}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Sestavení plné mapy (pokud je dost RAM)\n", "if BUILD_FULL:\n", " canvas = Image.new(\"RGB\", (W, H), (30, 30, 30))\n", "\n", " for f in tqdm(tile_files, desc=\"Plná mapa\"):\n", " m = re.match(r\"18_(\\d+)_(\\d+)\\.jpg\", f.name)\n", " if not m:\n", " continue\n", " xi = int(m.group(1)) - x_min\n", " yi = int(m.group(2)) - y_min\n", " try:\n", " tile = Image.open(f)\n", " canvas.paste(tile, (xi * TILE_SIZE, yi * TILE_SIZE))\n", " except Exception:\n", " pass\n", "\n", " canvas.save(OUTPUT_FULL, quality=88, optimize=True)\n", " print(f\"Plná mapa: {OUTPUT_FULL} ({W}×{H} px)\")\n", " del canvas\n", "else:\n", " print(\"Plná mapa přeskočena (nedostatek RAM).\")\n", " print(f\"Pro generování použij: python3 assemble_map.py\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Zobrazení preview mapy v notebooku\n", "import matplotlib.pyplot as plt\n", "import matplotlib.patches as mpatches\n", "\n", "preview = Image.open(OUTPUT_PREVIEW)\n", "\n", "fig, ax = plt.subplots(figsize=(16, 14))\n", "ax.imshow(preview)\n", "ax.set_title(f\"Hradec Králové — detekovaná vozidla\\n(preview {pw}×{ph} px)\", fontsize=14)\n", "ax.axis(\"off\")\n", "\n", "# Legenda\n", "legend_elements = [\n", " mpatches.Patch(facecolor='#00DC00', edgecolor='white', label='car'),\n", " mpatches.Patch(facecolor='#FFDC00', edgecolor='white', label='van'),\n", " mpatches.Patch(facecolor='#DC0000', edgecolor='white', label='truck'),\n", " mpatches.Patch(facecolor='#0078FF', edgecolor='white', label='bus'),\n", "]\n", "ax.legend(handles=legend_elements, loc='lower right', fontsize=12,\n", " framealpha=0.8, title='Typy vozidel')\n", "\n", "plt.tight_layout()\n", "plt.savefig(\"hk_map_overview.png\", dpi=100, bbox_inches=\"tight\")\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Souhrn výsledků\n", "with open(\"detections.json\") as f:\n", " all_detections = json.load(f)\n", "\n", "from collections import Counter\n", "CLASS_NAMES = [\"car\", \"van\", \"truck\", \"bus\"]\n", "counts = Counter()\n", "tiles_with_vehicles = 0\n", "\n", "for name, dets in all_detections.items():\n", " if dets:\n", " tiles_with_vehicles += 1\n", " for d in dets:\n", " counts[CLASS_NAMES[d[\"cls\"]]] += 1\n", "\n", "print(\"=\" * 40)\n", "print(\"SOUHRN — Hradec Králové\")\n", "print(\"=\" * 40)\n", "print(f\"Zpracované dlaždice: {len(all_detections):6d}\")\n", "print(f\"Dlaždice s vozidly: {tiles_with_vehicles:6d}\")\n", "print()\n", "print(\"Detekovaná vozidla:\")\n", "for cls in CLASS_NAMES:\n", " print(f\" {cls:10s}: {counts[cls]:6d}\")\n", "print(f\" {'CELKEM':10s}: {sum(counts.values()):6d}\")\n", "print(\"=\" * 40)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Sloupcový graf celkových počtů\n", "fig, ax = plt.subplots(figsize=(8, 5))\n", "\n", "bar_colors = [\"#00DC00\", \"#FFDC00\", \"#DC0000\", \"#0078FF\"]\n", "bars = ax.bar(CLASS_NAMES, [counts[c] for c in CLASS_NAMES], color=bar_colors, edgecolor=\"black\")\n", "\n", "for bar, cls in zip(bars, CLASS_NAMES):\n", " ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 5,\n", " str(counts[cls]), ha=\"center\", va=\"bottom\", fontsize=12, fontweight=\"bold\")\n", "\n", "ax.set_title(\"Počet detekovaných vozidel — Hradec Králové\", fontsize=14)\n", "ax.set_ylabel(\"Počet vozidel\")\n", "ax.set_xlabel(\"Typ vozidla\")\n", "plt.tight_layout()\n", "plt.savefig(\"vehicle_counts_chart.png\", dpi=150, bbox_inches=\"tight\")\n", "plt.show()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python", "version": "3.10.0" } }, "nbformat": 4, "nbformat_minor": 4 }