update
This commit is contained in:
@@ -6,15 +6,25 @@
|
||||
"source": [
|
||||
"# 01 — Příprava datasetu VisDrone\n",
|
||||
"\n",
|
||||
"**VisDrone2019-DET** je veřejně dostupný dataset leteckých snímků s anotacemi vozidel.\n",
|
||||
"**VisDrone2019-DET** je veřejně dostupný dataset dronových snímků s anotacemi vozidel.\n",
|
||||
"Obsahuje třídy: `pedestrian, people, bicycle, car, van, truck, tricycle, awning-tricycle, bus, motor`.\n",
|
||||
"\n",
|
||||
"My použijeme pouze vozidlové třídy a převedeme anotace do formátu YOLO.\n",
|
||||
"Použijeme pouze vozidlové třídy a převedeme anotace do formátu YOLO.\n",
|
||||
"\n",
|
||||
"**Citace:**\n",
|
||||
"> Zhu, P., Wen, L., Du, D., et al. (2021). Detection and Tracking Meet Drones Challenge. *IEEE TPAMI*."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-1d291f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Instalace závislostí\n",
|
||||
"\n",
|
||||
"Nainstalujeme potřebné knihovny: `ultralytics` (YOLOv8), `Pillow`, `tqdm`, `pandas` a `matplotlib` pro analýzu a vizualizaci."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
@@ -36,9 +46,19 @@
|
||||
"%pip install ultralytics Pillow tqdm requests pyyaml pandas matplotlib --quiet"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-75919d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Stažení VisDrone datasetu\n",
|
||||
"\n",
|
||||
"VisDrone2019-DET je rozdělený na tři části (train/val/test). Každá část obsahuje letecké snímky a textové anotace."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -47,13 +67,13 @@
|
||||
"text": [
|
||||
"Stahuji train.zip ...\n",
|
||||
"Rozbaluji train.zip ...\n",
|
||||
" OK → data/visdrone/VisDrone2019-DET-train\n",
|
||||
" OK data/visdrone/VisDrone2019-DET-train\n",
|
||||
"Stahuji val.zip ...\n",
|
||||
"Rozbaluji val.zip ...\n",
|
||||
" OK → data/visdrone/VisDrone2019-DET-val\n",
|
||||
" OK data/visdrone/VisDrone2019-DET-val\n",
|
||||
"Stahuji test.zip ...\n",
|
||||
"Rozbaluji test.zip ...\n",
|
||||
" OK → data/visdrone/VisDrone2019-DET-test-dev\n",
|
||||
" OK data/visdrone/VisDrone2019-DET-test-dev\n",
|
||||
"Dataset připraven.\n"
|
||||
]
|
||||
}
|
||||
@@ -66,7 +86,6 @@
|
||||
"DATA_DIR = Path(\"data/visdrone\")\n",
|
||||
"DATA_DIR.mkdir(parents=True, exist_ok=True)\n",
|
||||
"\n",
|
||||
"# Mapování split → název složky (VisDrone používá plné názvy)\n",
|
||||
"SPLIT_DIRS = {\n",
|
||||
" \"train\": DATA_DIR / \"VisDrone2019-DET-train\",\n",
|
||||
" \"val\": DATA_DIR / \"VisDrone2019-DET-val\",\n",
|
||||
@@ -91,11 +110,19 @@
|
||||
" print(f\"Rozbaluji {zip_path.name} ...\")\n",
|
||||
" with zipfile.ZipFile(zip_path) as zf:\n",
|
||||
" zf.extractall(DATA_DIR)\n",
|
||||
" print(f\" OK → {split_dir}\")\n",
|
||||
" print(f\" OK {split_dir}\")\n",
|
||||
"\n",
|
||||
"print(\"Dataset připraven.\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-c4fe96",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Ověření struktury datasetu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
@@ -122,9 +149,19 @@
|
||||
" print(f\"{split}: CHYBÍ ({split_dir})\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-f2ffb4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Formát anotací VisDrone\n",
|
||||
"\n",
|
||||
"Každá řádka anotačního souboru obsahuje: `bbox_left, bbox_top, bbox_width, bbox_height, score, category, truncation, occlusion`."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -149,8 +186,6 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Ukázkový anotační soubor\n",
|
||||
"# Formát: <bbox_left>,<bbox_top>,<bbox_width>,<bbox_height>,<score>,<category>,<truncation>,<occlusion>\n",
|
||||
"import pandas as pd\n",
|
||||
"\n",
|
||||
"ann_dir = SPLIT_DIRS[\"train\"] / \"annotations\"\n",
|
||||
@@ -162,9 +197,19 @@
|
||||
"print(\"\\nRozdělení tříd:\", df[\"cat\"].value_counts().to_dict())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-3f6c92",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Konverze anotací do formátu YOLO\n",
|
||||
"\n",
|
||||
"YOLO používá normalizované souřadnice středu ohraničovacího rámečku (cx, cy, w, h) v rozsahu 0–1. VisDrone má 10 tříd — my vybereme pouze 4 vozidlové: car, van, truck, bus. Pěší a cyklisté jsou pro tuto úlohu irelevantní."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -219,12 +264,11 @@
|
||||
"# 0=ignored, 1=pedestrian, 2=people, 3=bicycle, 4=car,\n",
|
||||
"# 5=van, 6=truck, 7=tricycle, 8=awning-tricycle, 9=bus, 10=motor, 11=others\n",
|
||||
"\n",
|
||||
"# Přemapování na 4 vozidlové třídy\n",
|
||||
"VISDRONE_TO_YOLO = {\n",
|
||||
" 4: 0, # car → car\n",
|
||||
" 5: 1, # van → van\n",
|
||||
" 6: 2, # truck → truck\n",
|
||||
" 9: 3, # bus → bus\n",
|
||||
" 4: 0, # car -> car\n",
|
||||
" 5: 1, # van -> van\n",
|
||||
" 6: 2, # truck -> truck\n",
|
||||
" 9: 3, # bus -> bus\n",
|
||||
"}\n",
|
||||
"CLASS_NAMES = [\"car\", \"van\", \"truck\", \"bus\"]\n",
|
||||
"\n",
|
||||
@@ -278,9 +322,19 @@
|
||||
"print(\"Konverze hotova!\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-4b68ce",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Konfigurace datasetu (YAML)\n",
|
||||
"\n",
|
||||
"YOLOv8 potřebuje YAML soubor popisující cestu k datům a názvy tříd. "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -302,7 +356,6 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Vytvoření YAML konfigurace pro YOLOv8\n",
|
||||
"import yaml\n",
|
||||
"\n",
|
||||
"cfg = {\n",
|
||||
@@ -321,9 +374,19 @@
|
||||
"print(yaml.dump(cfg, allow_unicode=True))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-a00834",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Vizualizace anotací\n",
|
||||
"\n",
|
||||
"Vybereme několik trénovacích snímků a zobrazíme jejich anotace — každá třída je vyznačena jinou barvou rámečku."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -345,7 +408,6 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Vizualizace ukázkových anotací\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import matplotlib.patches as patches\n",
|
||||
"import random\n",
|
||||
@@ -397,9 +459,17 @@
|
||||
"show_sample()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-663f41",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Statistiky datasetu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -424,7 +494,6 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Statistiky datasetu\n",
|
||||
"from collections import Counter\n",
|
||||
"\n",
|
||||
"for split in [\"train\", \"val\"]:\n",
|
||||
|
||||
@@ -12,6 +12,16 @@
|
||||
"**GPU doporučeno** (trénink na CPU trvá ~10× déle)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-8d392d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Kontrola prostředí\n",
|
||||
"\n",
|
||||
"Zjistíme, zda je k dispozici GPU (CUDA) nebo Apple Silicon (MPS), a vypíšeme verzi PyTorche. Trénink na GPU je přibližně 10× rychlejší než na CPU."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
@@ -43,9 +53,19 @@
|
||||
" print(\"MPS (Apple Silicon) dostupné\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-af9159",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Načtení předtrénovaného modelu\n",
|
||||
"\n",
|
||||
"Použijeme YOLOv8s (small) s váhami předtrénovanými na ImageNet. Tento model budeme fine-tunovat na VisDrone dataset."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -68,12 +88,20 @@
|
||||
"YAML = Path(\"data/yolo_visdrone/dataset.yaml\")\n",
|
||||
"assert YAML.exists(), f\"Nejprve spusť 01_dataset_prep.ipynb! Chybí: {YAML}\"\n",
|
||||
"\n",
|
||||
"# Zvolíme YOLOv8s — dobrý poměr přesnosti a rychlosti\n",
|
||||
"# Alternativy: yolov8n (nejrychlejší), yolov8m (přesnější)\n",
|
||||
"model = YOLO(\"models/yolov8s.pt\") # stáhne předtrénované ImageNet váhy\n",
|
||||
"model = YOLO(\"models/yolov8s.pt\") \n",
|
||||
"print(\"Model načten:\", model.info())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-58715d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Trénování modelu\n",
|
||||
"\n",
|
||||
"Spustíme trénink na 50 epoch s early stoppingem (patience=10). Augmentace (rotace, překlápění, mosaic) pomáhají modelu generalizovat na různé letecké pohledy a světelné podmínky."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
@@ -83,7 +111,7 @@
|
||||
"import torch\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"# Detekce dostupného zařízení\n",
|
||||
"\n",
|
||||
"if torch.cuda.is_available():\n",
|
||||
" DEVICE = \"cuda\"\n",
|
||||
"elif torch.backends.mps.is_available():\n",
|
||||
@@ -106,22 +134,30 @@
|
||||
" save_period=10,\n",
|
||||
" val=True,\n",
|
||||
" plots=True,\n",
|
||||
" # Augmentace pro letecké snímky\n",
|
||||
" degrees=15.0, # rotace\n",
|
||||
" flipud=0.5, # vertikální flip\n",
|
||||
" degrees=15.0,\n",
|
||||
" flipud=0.5,\n",
|
||||
" fliplr=0.5,\n",
|
||||
" mosaic=1.0,\n",
|
||||
" scale=0.5,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-1fc179",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Výsledky tréninku\n",
|
||||
"\n",
|
||||
"Zobrazíme grafy průběhu trénování (loss, mAP), matici záměn a příklady predikcí na validační sadě."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Zobrazení tréninkových grafů\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import matplotlib.image as mpimg\n",
|
||||
"from pathlib import Path\n",
|
||||
@@ -142,11 +178,12 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-021cf0",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Fine-tuning na Vrchlabí (volitelné)\n",
|
||||
"## Validace nejlepšího modelu\n",
|
||||
"\n",
|
||||
"Pokud existuje `dataset_vrchlabi.zip` nebo rozbalená složka `vrchlabi/`, provede se fine-tuning předchozího modelu na vlastních datech z Vrchlabí."
|
||||
"Načteme nejlepší checkpoint z tréninku a spustíme plnou validaci na validační sadě. Sledujeme metriku mAP50, která je standardem pro detekci objektů."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -169,6 +206,16 @@
|
||||
" print(f\" AP50[{cls}]: {ap:.4f}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-f05d8c",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Fine-tuning na vlastních datech (Vrchlabí)\n",
|
||||
"\n",
|
||||
"Pokud je k dispozici lokálně anotovaný dataset z Vrchlabí, provedeme fine-tuning modelu natrénovaného na VisDrone. Tímto krokem model přizpůsobíme konkrétním podmínkám českých měst."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
@@ -182,7 +229,6 @@
|
||||
"VRCHLABI_ZIP = Path('dataset_vrchlabi.zip')\n",
|
||||
"VRCHLABI_YAML = Path('vrchlabi/dataset.yaml')\n",
|
||||
"\n",
|
||||
"# Rozbal zip pokud yaml ještě není\n",
|
||||
"if VRCHLABI_ZIP.exists() and not VRCHLABI_YAML.exists():\n",
|
||||
" print('Rozbaluji dataset_vrchlabi.zip ...')\n",
|
||||
" with zipfile.ZipFile(VRCHLABI_ZIP) as zf:\n",
|
||||
@@ -195,12 +241,11 @@
|
||||
"else:\n",
|
||||
" print(f'Fine-tuning na: {VRCHLABI_YAML}')\n",
|
||||
"\n",
|
||||
" # Záloha modelu před fine-tuningem\n",
|
||||
" backup = best_weights.parent / 'best_before_finetune.pt'\n",
|
||||
" shutil.copy(best_weights, backup)\n",
|
||||
" print(f'Záloha uložena: {backup}')\n",
|
||||
"\n",
|
||||
" vrchlabi_model = YOLO(str(best_weights)) # navazuje na VisDrone trénink\n",
|
||||
" vrchlabi_model = YOLO(str(best_weights))\n",
|
||||
"\n",
|
||||
" vrchlabi_results = vrchlabi_model.train(\n",
|
||||
" data=str(VRCHLABI_YAML),\n",
|
||||
@@ -218,16 +263,26 @@
|
||||
" flipud=0.5,\n",
|
||||
" fliplr=0.5,\n",
|
||||
" mosaic=0.5,\n",
|
||||
" lr0=0.001, # nižší LR pro fine-tuning\n",
|
||||
" lr0=0.001,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" best_weights = Path(vrchlabi_results.save_dir) / 'weights' / 'best.pt'\n",
|
||||
" print(f'Vrchlabí fine-tuning hotov. Nejlepší váhy: {best_weights}')"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-805d37",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Uložení konfigurace modelu\n",
|
||||
"\n",
|
||||
"Cestu k nejlepšímu modelu zapíšeme do `models/model_config.json`, odkud ji načtou následující notebooky."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -240,7 +295,6 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Uložení cesty k nejlepšímu modelu pro další notebooky\n",
|
||||
"import json\n",
|
||||
"\n",
|
||||
"config = {\"best_model\": str(best_weights.resolve())}\n",
|
||||
|
||||
@@ -23,6 +23,16 @@
|
||||
"| bus | modrá |"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-83b798",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Načtení modelů\n",
|
||||
"\n",
|
||||
"Načteme oba modely: `best_finetuned.pt` (po fine-tuningu na Vrchlabí) a `best_before_finetune.pt` (pouze VisDrone). Porovnání obou modelů ukáže, jak moc fine-tuning zlepšil detekci na českých datech."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
@@ -59,9 +69,19 @@
|
||||
"print(f\"Base: {MODEL_BASE}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-dc5c89",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Konfigurace inference\n",
|
||||
"\n",
|
||||
"Nastavíme vstupní a výstupní adresáře, barevné kódování tříd (zelená=car, žlutá=van, červená=truck, modrá=bus), confidence threshold a velikost dávky. Dlaždice jsou 256×256 px odpovídající zoom level 18 mapových dlaždic."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -82,9 +102,6 @@
|
||||
"ANNOTATED_DIR_FINETUNED.mkdir(exist_ok=True)\n",
|
||||
"ANNOTATED_DIR_BASE.mkdir(exist_ok=True)\n",
|
||||
"\n",
|
||||
"# Zpětná kompatibilita — hlavní vrstva je finetuned\n",
|
||||
"ANNOTATED_DIR = ANNOTATED_DIR_FINETUNED\n",
|
||||
"\n",
|
||||
"# Barvy pro každou třídu (RGB)\n",
|
||||
"CLASS_COLORS = {\n",
|
||||
" 0: (0, 220, 0), # car — zelená\n",
|
||||
@@ -110,9 +127,19 @@
|
||||
"print(f\"Zařízení: {DEVICE}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-452ad1",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Inferenční smyčka\n",
|
||||
"\n",
|
||||
"Pro každou dlaždici spustíme detekci objektů oběma modely. Detekovaná vozidla vyznačíme barevnými rámečky přímo do obrázku dlaždice a výsledky uložíme ve formátu JSON pro pozdější sestavení mapy."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -189,7 +216,6 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"def run_inference(model, annotated_dir: Path, label: str) -> dict:\n",
|
||||
" \"\"\"Spustí inferenci na všech dlaždicích, uloží anotované obrázky a vrátí dict detekcí.\"\"\"\n",
|
||||
" detections = {}\n",
|
||||
" vehicle_counts = defaultdict(int)\n",
|
||||
"\n",
|
||||
@@ -234,6 +260,16 @@
|
||||
"all_detections_base = run_inference(model_base, ANNOTATED_DIR_BASE, \"base\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-3e6ffd",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Uložení výsledků detekcí\n",
|
||||
"\n",
|
||||
"Detekce z obou modelů uložíme do JSON souborů. Každý záznam obsahuje název dlaždice a seznam detekcí s třídou, souřadnicemi rámečku a confidence score."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
@@ -265,9 +301,19 @@
|
||||
"print(f\" detections_base.json ({len(all_detections_base)} dlaždic)\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-95a7d7",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Vizualizace ukázkových dlaždic\n",
|
||||
"\n",
|
||||
"Náhodně vybereme 6 dlaždic s detekcemi a zobrazíme je — tak si ověříme kvalitu výsledků finetuned modelu."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -298,7 +344,6 @@
|
||||
"print(f\"[Finetuned] Dlaždice s detekcemi: {len(tiles_with_detections_ft)} / {len(all_detections_finetuned)}\")\n",
|
||||
"print(f\"[Base] Dlaždice s detekcemi: {len(tiles_with_detections_base)} / {len(all_detections_base)}\")\n",
|
||||
"\n",
|
||||
"# Ukázkové dlaždice — finetuned model\n",
|
||||
"samples = random.sample(tiles_with_detections_ft, min(6, len(tiles_with_detections_ft)))\n",
|
||||
"fig, axes = plt.subplots(2, 3, figsize=(15, 10))\n",
|
||||
"\n",
|
||||
@@ -314,6 +359,16 @@
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-960e2d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Heatmapa hustoty vozidel\n",
|
||||
"\n",
|
||||
"Z detekcí sestavíme heatmapu — každý pixel odpovídá jedné dlaždici a jeho barva udává počet detekovaných vozidel. Porovnáme oba modely vedle sebe."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
|
||||
@@ -8,14 +8,24 @@
|
||||
"\n",
|
||||
"Složíme otagované dlaždice (z notebooku 03) do velkých leteckých snímků HK pro **dvě vrstvy**:\n",
|
||||
"\n",
|
||||
"| Vrstva | Zdroj dlaždic | Výstup (preview) | Výstup (plný) |\n",
|
||||
"|--------|--------------|-----------------|--------------|\n",
|
||||
"| Finetuned model | `tiles_annotated_finetuned/` | `hk_finetuned_preview.jpg` | `hk_finetuned_full.jpg` |\n",
|
||||
"| Base model | `tiles_annotated_base/` | `hk_base_preview.jpg` | `hk_base_full.jpg` |\n",
|
||||
"| Vrstva | Zdroj dlaždic | Výstup (plný) |\n",
|
||||
"|--------|--------------|--------------|\n",
|
||||
"| Finetuned model | `tiles_annotated_finetuned/` | `hk_finetuned_full.jpg` |\n",
|
||||
"| Base model | `tiles_annotated_base/` | `hk_base_full.jpg` |\n",
|
||||
"\n",
|
||||
"Výsledná mapa: **74 × 69 dlaždic × 256 px = 18 944 × 17 664 px**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-a222d4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Příprava mřížky dlaždic\n",
|
||||
"\n",
|
||||
"Názvy dlaždic kódují jejich pozici ve formátu `18_X_Y.jpg` (tile schema XYZ, zoom 18). Z těchto souřadnic odvodíme rozměry výsledné mapy: počet sloupců, řádků a výsledné rozlišení v pixelech."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
@@ -46,9 +56,7 @@
|
||||
" assert d.exists(), f\"Nejprve spusť 03_inference_tiles.ipynb! (chybí {d})\"\n",
|
||||
"\n",
|
||||
"TILE_SIZE = 256\n",
|
||||
"PREVIEW_SCALE = 4\n",
|
||||
"\n",
|
||||
"# Rozsah mřížky odvoď z finetuned (obě mají stejné dlaždice)\n",
|
||||
"tile_files = sorted(ANNOTATED_DIR_FINETUNED.glob(\"18_*.jpg\"))\n",
|
||||
"xs, ys = [], []\n",
|
||||
"for f in tile_files:\n",
|
||||
@@ -68,82 +76,49 @@
|
||||
"print(f\"Odhadovaná velikost jedné mapy (JPEG 88%): ~{W*H*3/1e6/10:.0f} MB\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-a8c48b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Sestavení map\n",
|
||||
"\n",
|
||||
"Pro obě vrstvy (finetuned a base model) složíme dlaždice do jednoho velkého obrázku."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Dostupná RAM: 6.8 GB\n",
|
||||
"Potřeba pro jednu plnou mapu: 1.0 GB (dvě: 2.0 GB)\n",
|
||||
"RAM dostačuje — generuji plné mapy pro obě vrstvy\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import psutil\n",
|
||||
"\n",
|
||||
"ram_gb = psutil.virtual_memory().available / 1e9\n",
|
||||
"needed_gb = W * H * 3 / 1e9 # jedna mapa; dvě = 2×\n",
|
||||
"\n",
|
||||
"print(f\"Dostupná RAM: {ram_gb:.1f} GB\")\n",
|
||||
"print(f\"Potřeba pro jednu plnou mapu: {needed_gb:.1f} GB (dvě: {2*needed_gb:.1f} GB)\")\n",
|
||||
"\n",
|
||||
"BUILD_FULL = needed_gb <= ram_gb * 0.5 # nech puffer pro druhou mapu\n",
|
||||
"if BUILD_FULL:\n",
|
||||
" print(\"RAM dostačuje — generuji plné mapy pro obě vrstvy\")\n",
|
||||
"else:\n",
|
||||
" print(\"Nedostatek RAM — generuji pouze preview mapy\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "a375151f0f8f4fd5ac94ecb5567c9849",
|
||||
"model_id": "08bd8aac266e47808fd9018d7788e249",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
"Preview finetuned: 0%| | 0/5106 [00:00<?, ?it/s]"
|
||||
"Plná mapa finetuned: 0%| | 0/5106 [00:00<?, ?it/s]"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Uloženo: hk_finetuned_preview.jpg (4736×4416 px)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "8f83829d65db4bb9b3399d07711f726e",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
"Preview base: 0%| | 0/5106 [00:00<?, ?it/s]"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Uloženo: hk_base_preview.jpg (4736×4416 px)\n"
|
||||
"ename": "KeyboardInterrupt",
|
||||
"evalue": "",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
|
||||
"\u001b[31mKeyboardInterrupt\u001b[39m Traceback (most recent call last)",
|
||||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 34\u001b[39m\n\u001b[32m 30\u001b[39m print()\n\u001b[32m 31\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m canvas\n\u001b[32m 32\u001b[39m \n\u001b[32m 33\u001b[39m \n\u001b[32m---> \u001b[39m\u001b[32m34\u001b[39m assemble_map(ANNOTATED_DIR_FINETUNED, Path(\"output/hk_finetuned_full.jpg\"),\n\u001b[32m 35\u001b[39m scale=\u001b[32m1\u001b[39m, desc=\u001b[33m\"Plná mapa finetuned\"\u001b[39m)\n\u001b[32m 36\u001b[39m assemble_map(ANNOTATED_DIR_BASE, Path(\"output/hk_base_full.jpg\"),\n\u001b[32m 37\u001b[39m scale=\u001b[32m1\u001b[39m, desc=\u001b[33m\"Plná mapa base\"\u001b[39m)\n",
|
||||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 26\u001b[39m, in \u001b[36massemble_map\u001b[39m\u001b[34m(annotated_dir, output_path, scale, desc)\u001b[39m\n\u001b[32m 22\u001b[39m canvas.paste(tile, (xi * ts, yi * ts))\n\u001b[32m 23\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m Exception:\n\u001b[32m 24\u001b[39m missing += \u001b[32m1\u001b[39m\n\u001b[32m 25\u001b[39m \n\u001b[32m---> \u001b[39m\u001b[32m26\u001b[39m canvas.save(output_path, quality=\u001b[32m88\u001b[39m, optimize=\u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[32m 27\u001b[39m print(f\"Uloženo: {output_path} ({pw}×{ph} px)\", end=\u001b[33m\"\"\u001b[39m)\n\u001b[32m 28\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m missing:\n\u001b[32m 29\u001b[39m print(f\" [{missing} chybějících dlaždic]\", end=\u001b[33m\"\"\u001b[39m)\n",
|
||||
"\u001b[36mFile \u001b[39m\u001b[32m~/workspace/su2-2/.venv/lib/python3.14/site-packages/PIL/Image.py:2713\u001b[39m, in \u001b[36mImage.save\u001b[39m\u001b[34m(self, fp, format, **params)\u001b[39m\n\u001b[32m 2710\u001b[39m fp = cast(IO[\u001b[38;5;28mbytes\u001b[39m], fp)\n\u001b[32m 2712\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m2713\u001b[39m \u001b[43msave_handler\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfp\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfilename\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 2714\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mException\u001b[39;00m:\n\u001b[32m 2715\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m open_fp:\n",
|
||||
"\u001b[36mFile \u001b[39m\u001b[32m~/workspace/su2-2/.venv/lib/python3.14/site-packages/PIL/JpegImagePlugin.py:847\u001b[39m, in \u001b[36m_save\u001b[39m\u001b[34m(im, fp, filename)\u001b[39m\n\u001b[32m 842\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 843\u001b[39m \u001b[38;5;66;03m# The EXIF info needs to be written as one block, + APP1, + one spare byte.\u001b[39;00m\n\u001b[32m 844\u001b[39m \u001b[38;5;66;03m# Ensure that our buffer is big enough. Same with the icc_profile block.\u001b[39;00m\n\u001b[32m 845\u001b[39m bufsize = \u001b[38;5;28mmax\u001b[39m(\u001b[38;5;28mlen\u001b[39m(exif) + \u001b[32m5\u001b[39m, \u001b[38;5;28mlen\u001b[39m(extra) + \u001b[32m1\u001b[39m)\n\u001b[32m--> \u001b[39m\u001b[32m847\u001b[39m \u001b[43mImageFile\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_save\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 848\u001b[39m \u001b[43m \u001b[49m\u001b[43mim\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfp\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[43mImageFile\u001b[49m\u001b[43m.\u001b[49m\u001b[43m_Tile\u001b[49m\u001b[43m(\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mjpeg\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m(\u001b[49m\u001b[32;43m0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[32;43m0\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[43m+\u001b[49m\u001b[43m \u001b[49m\u001b[43mim\u001b[49m\u001b[43m.\u001b[49m\u001b[43msize\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[32;43m0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrawmode\u001b[49m\u001b[43m)\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mbufsize\u001b[49m\n\u001b[32m 849\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
|
||||
"\u001b[36mFile \u001b[39m\u001b[32m~/workspace/su2-2/.venv/lib/python3.14/site-packages/PIL/ImageFile.py:665\u001b[39m, in \u001b[36m_save\u001b[39m\u001b[34m(im, fp, tile, bufsize)\u001b[39m\n\u001b[32m 663\u001b[39m fh = fp.fileno()\n\u001b[32m 664\u001b[39m fp.flush()\n\u001b[32m--> \u001b[39m\u001b[32m665\u001b[39m \u001b[43m_encode_tile\u001b[49m\u001b[43m(\u001b[49m\u001b[43mim\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfp\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtile\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mbufsize\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfh\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 666\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m (\u001b[38;5;167;01mAttributeError\u001b[39;00m, io.UnsupportedOperation) \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[32m 667\u001b[39m _encode_tile(im, fp, tile, bufsize, \u001b[38;5;28;01mNone\u001b[39;00m, exc)\n",
|
||||
"\u001b[36mFile \u001b[39m\u001b[32m~/workspace/su2-2/.venv/lib/python3.14/site-packages/PIL/ImageFile.py:700\u001b[39m, in \u001b[36m_encode_tile\u001b[39m\u001b[34m(im, fp, tile, bufsize, fh, exc)\u001b[39m\n\u001b[32m 697\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 698\u001b[39m \u001b[38;5;66;03m# slight speedup: compress to real file object\u001b[39;00m\n\u001b[32m 699\u001b[39m \u001b[38;5;28;01massert\u001b[39;00m fh \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m700\u001b[39m errcode = \u001b[43mencoder\u001b[49m\u001b[43m.\u001b[49m\u001b[43mencode_to_file\u001b[49m\u001b[43m(\u001b[49m\u001b[43mfh\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mbufsize\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 701\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m errcode < \u001b[32m0\u001b[39m:\n\u001b[32m 702\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m _get_oserror(errcode, encoder=\u001b[38;5;28;01mTrue\u001b[39;00m) \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mexc\u001b[39;00m\n",
|
||||
"\u001b[31mKeyboardInterrupt\u001b[39m: "
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -181,75 +156,25 @@
|
||||
" return canvas\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# --- Preview mapy (vždy) ---\n",
|
||||
"preview_ft = assemble_map(ANNOTATED_DIR_FINETUNED, Path(\"output/hk_finetuned_preview.jpg\"),\n",
|
||||
" scale=PREVIEW_SCALE, desc=\"Preview finetuned\")\n",
|
||||
"preview_base = assemble_map(ANNOTATED_DIR_BASE, Path(\"output/hk_base_preview.jpg\"),\n",
|
||||
" scale=PREVIEW_SCALE, desc=\"Preview base\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "348dac183a834470b930654ef862a4f2",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
"Plná mapa finetuned: 0%| | 0/5106 [00:00<?, ?it/s]"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Uloženo: hk_finetuned_full.jpg (18944×17664 px)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "dd4e5219605a4b1a99b9eb0f3f2e0cc4",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
"Plná mapa base: 0%| | 0/5106 [00:00<?, ?it/s]"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Uloženo: hk_base_full.jpg (18944×17664 px)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# --- Plné mapy (pokud je dost RAM) ---\n",
|
||||
"if BUILD_FULL:\n",
|
||||
" assemble_map(ANNOTATED_DIR_FINETUNED, Path(\"output/hk_finetuned_full.jpg\"),\n",
|
||||
"assemble_map(ANNOTATED_DIR_FINETUNED, Path(\"output/hk_finetuned_full.jpg\"),\n",
|
||||
" scale=1, desc=\"Plná mapa finetuned\")\n",
|
||||
" assemble_map(ANNOTATED_DIR_BASE, Path(\"output/hk_base_full.jpg\"),\n",
|
||||
" scale=1, desc=\"Plná mapa base\")\n",
|
||||
"else:\n",
|
||||
" print(\"Plné mapy přeskočeny (nedostatek RAM).\")"
|
||||
"assemble_map(ANNOTATED_DIR_BASE, Path(\"output/hk_base_full.jpg\"),\n",
|
||||
" scale=1, desc=\"Plná mapa base\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-3f3428",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Srovnání map vedle sebe\n",
|
||||
"\n",
|
||||
"Zobrazíme preview mapy obou modelů vedle sebe s legendou barev vozidel. Vizuální srovnání odhalí rozdíly v citlivosti modelů na různé části města."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -275,16 +200,16 @@
|
||||
"]\n",
|
||||
"\n",
|
||||
"fig, axes = plt.subplots(1, 2, figsize=(28, 13))\n",
|
||||
"pw = W // PREVIEW_SCALE\n",
|
||||
"ph = H // PREVIEW_SCALE\n",
|
||||
"pw = W\n",
|
||||
"ph = H\n",
|
||||
"\n",
|
||||
"for ax, img_path, title in zip(\n",
|
||||
" axes,\n",
|
||||
" [\"output/hk_finetuned_preview.jpg\", \"output/hk_base_preview.jpg\"],\n",
|
||||
" [\"output/hk_finetuned_full.jpg\", \"output/hk_base_full.jpg\"],\n",
|
||||
" [\"Finetuned model\", \"Base model (před fine-tuningem)\"],\n",
|
||||
"):\n",
|
||||
" ax.imshow(Image.open(img_path))\n",
|
||||
" ax.set_title(f\"Hradec Králové — {title}\\n(preview {pw}×{ph} px)\", fontsize=13)\n",
|
||||
" ax.set_title(f\"Hradec Králové — {title}\\n({pw}×{ph} px)\", fontsize=13)\n",
|
||||
" ax.axis(\"off\")\n",
|
||||
" ax.legend(handles=legend_elements, loc=\"lower right\", fontsize=10,\n",
|
||||
" framealpha=0.8, title=\"Typy vozidel\")\n",
|
||||
@@ -294,9 +219,19 @@
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-0db3ae",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Souhrn počtů vozidel\n",
|
||||
"\n",
|
||||
"Z JSON souborů s detekcemi spočítáme celkový počet vozidel každé třídy pro oba modely a zobrazíme tabulku."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -360,9 +295,19 @@
|
||||
"print(\"=\" * 52)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-ba2e9f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Srovnávací sloupcový graf\n",
|
||||
"\n",
|
||||
"Graf porovnává počty detekovaných vozidel podle třídy pro oba modely. Vidíme, které třídy detekuje finetuned model lépe a kde je naopak méně citlivý."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -377,7 +322,6 @@
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Srovnávací sloupcový graf: finetuned vs base\n",
|
||||
"bar_colors = [\"#00DC00\", \"#FFDC00\", \"#DC0000\", \"#0078FF\"]\n",
|
||||
"x = np.arange(len(CLASS_NAMES))\n",
|
||||
"width = 0.35\n",
|
||||
@@ -405,6 +349,134 @@
|
||||
"plt.savefig(\"output/vehicle_counts_comparison.png\", dpi=150, bbox_inches=\"tight\")\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "md-webserver",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Interaktivní mapa\n",
|
||||
"\n",
|
||||
"Spustíme jednoduchý HTTP server, který zpřístupní interaktivní mapu v prohlížeči.\n",
|
||||
"\n",
|
||||
"Po spuštění buňky otevři odkaz níže v prohlížeči."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "code-webserver",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "OSError",
|
||||
"evalue": "[Errno 48] Address already in use",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
|
||||
"\u001b[31mOSError\u001b[39m Traceback (most recent call last)",
|
||||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[5]\u001b[39m\u001b[32m, line 62\u001b[39m\n\u001b[32m 58\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[33m\"_tile_server\"\u001b[39m \u001b[38;5;28;01min\u001b[39;00m globals() \u001b[38;5;28;01mand\u001b[39;00m _tile_server \u001b[38;5;28;01mis\u001b[39;00m \u001b[38;5;28;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 59\u001b[39m _tile_server.shutdown()\n\u001b[32m 60\u001b[39m print(\u001b[33m\"Předchozí server zastaven.\"\u001b[39m)\n\u001b[32m 61\u001b[39m \n\u001b[32m---> \u001b[39m\u001b[32m62\u001b[39m _tile_server = http.server.HTTPServer((\u001b[33m\"\"\u001b[39m, PORT), TileHandler)\n\u001b[32m 63\u001b[39m thread = threading.Thread(target=_tile_server.serve_forever, daemon=\u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[32m 64\u001b[39m thread.start()\n\u001b[32m 65\u001b[39m \n",
|
||||
"\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/lib/python3.14/socketserver.py:457\u001b[39m, in \u001b[36mTCPServer.__init__\u001b[39m\u001b[34m(self, server_address, RequestHandlerClass, bind_and_activate)\u001b[39m\n\u001b[32m 455\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m bind_and_activate:\n\u001b[32m 456\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m457\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mserver_bind\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 458\u001b[39m \u001b[38;5;28mself\u001b[39m.server_activate()\n\u001b[32m 459\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m:\n",
|
||||
"\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/lib/python3.14/http/server.py:148\u001b[39m, in \u001b[36mHTTPServer.server_bind\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 146\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mserver_bind\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[32m 147\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Override server_bind to store the server name.\"\"\"\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m148\u001b[39m \u001b[43msocketserver\u001b[49m\u001b[43m.\u001b[49m\u001b[43mTCPServer\u001b[49m\u001b[43m.\u001b[49m\u001b[43mserver_bind\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 149\u001b[39m host, port = \u001b[38;5;28mself\u001b[39m.server_address[:\u001b[32m2\u001b[39m]\n\u001b[32m 150\u001b[39m \u001b[38;5;28mself\u001b[39m.server_name = socket.getfqdn(host)\n",
|
||||
"\u001b[36mFile \u001b[39m\u001b[32m/opt/homebrew/Cellar/python@3.14/3.14.4/Frameworks/Python.framework/Versions/3.14/lib/python3.14/socketserver.py:478\u001b[39m, in \u001b[36mTCPServer.server_bind\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 473\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m (\n\u001b[32m 474\u001b[39m \u001b[38;5;28mself\u001b[39m.allow_reuse_port \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mhasattr\u001b[39m(socket, \u001b[33m\"\u001b[39m\u001b[33mSO_REUSEPORT\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 475\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m.address_family \u001b[38;5;129;01min\u001b[39;00m (socket.AF_INET, socket.AF_INET6)\n\u001b[32m 476\u001b[39m ):\n\u001b[32m 477\u001b[39m \u001b[38;5;28mself\u001b[39m.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, \u001b[32m1\u001b[39m)\n\u001b[32m--> \u001b[39m\u001b[32m478\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43msocket\u001b[49m\u001b[43m.\u001b[49m\u001b[43mbind\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mserver_address\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 479\u001b[39m \u001b[38;5;28mself\u001b[39m.server_address = \u001b[38;5;28mself\u001b[39m.socket.getsockname()\n",
|
||||
"\u001b[31mOSError\u001b[39m: [Errno 48] Address already in use"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import threading\n",
|
||||
"import http.server\n",
|
||||
"import webbrowser\n",
|
||||
"import re\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"PORT = 8765\n",
|
||||
"ROOT = Path(\".\").parent.resolve()\n",
|
||||
"\n",
|
||||
"TILE_DIRS = {\n",
|
||||
" \"tiles\": ROOT / \"tiles\",\n",
|
||||
" \"tiles_annotated_finetuned\": ROOT / \"output\" / \"tiles_annotated_finetuned\",\n",
|
||||
" \"tiles_annotated_base\": ROOT / \"output\" / \"tiles_annotated_base\",\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"class TileHandler(http.server.BaseHTTPRequestHandler):\n",
|
||||
" def log_message(self, *args):\n",
|
||||
" pass # potlač výpisy každého requestu\n",
|
||||
"\n",
|
||||
" def do_GET(self):\n",
|
||||
" path = self.path.split(\"?\")[0]\n",
|
||||
"\n",
|
||||
" # /tiles_annotated_finetuned/18/X/Y.jpg → flat file 18_X_Y.jpg\n",
|
||||
" m = re.match(r\"^/([^/]+)/(\\d+)/(\\d+)/(\\d+)\\.jpg$\", path)\n",
|
||||
" if m:\n",
|
||||
" prefix, z, x, y = m.group(1), m.group(2), m.group(3), m.group(4)\n",
|
||||
" tile_dir = TILE_DIRS.get(prefix)\n",
|
||||
" if tile_dir:\n",
|
||||
" tile_file = tile_dir / f\"{z}_{x}_{y}.jpg\"\n",
|
||||
" if tile_file.exists():\n",
|
||||
" data = tile_file.read_bytes()\n",
|
||||
" self.send_response(200)\n",
|
||||
" self.send_header(\"Content-Type\", \"image/jpeg\")\n",
|
||||
" self.send_header(\"Content-Length\", str(len(data)))\n",
|
||||
" self.end_headers()\n",
|
||||
" self.wfile.write(data)\n",
|
||||
" return\n",
|
||||
" self.send_error(404)\n",
|
||||
" return\n",
|
||||
"\n",
|
||||
" # /output/map.html nebo / → static file\n",
|
||||
" if path == \"/\" or path == \"\":\n",
|
||||
" path = \"/output/map.html\"\n",
|
||||
" file_path = ROOT / path.lstrip(\"/\")\n",
|
||||
" if file_path.exists() and file_path.is_file():\n",
|
||||
" suffix = file_path.suffix.lower()\n",
|
||||
" ct = {\"html\": \"text/html\", \"css\": \"text/css\", \"js\": \"application/javascript\"}.get(suffix[1:], \"application/octet-stream\")\n",
|
||||
" data = file_path.read_bytes()\n",
|
||||
" self.send_response(200)\n",
|
||||
" self.send_header(\"Content-Type\", ct)\n",
|
||||
" self.send_header(\"Content-Length\", str(len(data)))\n",
|
||||
" self.end_headers()\n",
|
||||
" self.wfile.write(data)\n",
|
||||
" else:\n",
|
||||
" self.send_error(404)\n",
|
||||
"\n",
|
||||
"# Zastav předchozí instanci serveru (pokud existuje)\n",
|
||||
"if \"_tile_server\" in globals() and _tile_server is not None:\n",
|
||||
" _tile_server.shutdown()\n",
|
||||
" print(\"Předchozí server zastaven.\")\n",
|
||||
"\n",
|
||||
"_tile_server = http.server.HTTPServer((\"\", PORT), TileHandler)\n",
|
||||
"thread = threading.Thread(target=_tile_server.serve_forever, daemon=True)\n",
|
||||
"thread.start()\n",
|
||||
"\n",
|
||||
"url = f\"http://localhost:{PORT}\"\n",
|
||||
"print(f\"Server běží na {url}\")\n",
|
||||
"print(f\"Otevři v prohlížeči: {url}\")\n",
|
||||
"webbrowser.open(url)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Server neběží.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Zastav server\n",
|
||||
"if \"_tile_server\" in globals() and _tile_server is not None:\n",
|
||||
" _tile_server.shutdown()\n",
|
||||
" _tile_server = None\n",
|
||||
" print(\"Server zastaven.\")\n",
|
||||
"else:\n",
|
||||
" print(\"Server neběží.\")\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
Reference in New Issue
Block a user