coderuday21 commited on
Commit
4e9c6ba
·
1 Parent(s): 7186eb1

Add pothole detection as new menu type with separate engine and research plan

Browse files
Pothole_Detection_Integration_Plan.md ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Pothole Detection Integration (Research + Architecture)
2
+
3
+ ## 1) Computer vision approaches for road damage detection
4
+
5
+ Typical successful families:
6
+
7
+ - **Object detection (bounding boxes)**: YOLOv5/YOLOv8, Faster R-CNN
8
+ - Pros: simple outputs, fast, easy UI.
9
+ - Cons: boxes are coarse; struggles with thin cracks and complex shapes.
10
+
11
+ - **Instance segmentation**: Mask R-CNN, YOLACT
12
+ - Pros: tighter region boundary and size estimation.
13
+ - Cons: heavier models, more training complexity.
14
+
15
+ - **Semantic segmentation**: U-Net / DeepLabv3+ / SegFormer
16
+ - Pros: best for pixel-level damage maps, severity estimation.
17
+ - Cons: needs mask labels; inference cost.
18
+
19
+ - **Two-stage pipelines**:
20
+ 1) road surface ROI extraction (segment road), then
21
+ 2) damage detection inside road only
22
+ - Pros: reduces false positives (buildings, shadows, non-road textures).
23
+
24
+ ## 2) Datasets and pretrained models (starting points)
25
+
26
+ Common public datasets (road damage + potholes):
27
+
28
+ - **RDD (Road Damage Dataset / Road Damage Detection)**
29
+ Includes potholes and other damage classes from multiple countries.
30
+
31
+ - **Pothole-600 / Pothole datasets on Kaggle**
32
+ Smaller but useful for prototyping.
33
+
34
+ - **CrackForest / CFD / other crack datasets**
35
+ More focused on cracks; can help pretraining for surface defects.
36
+
37
+ Practical approach:
38
+ - Use a model pretrained on COCO, then fine-tune on RDD/pothole datasets.
39
+ - For best results, fine-tune on **your region-specific imagery** (road texture and lighting differs).
40
+
41
+ ## 3) Feasibility: drone vs satellite vs vehicle camera
42
+
43
+ - **Vehicle camera (recommended)**:
44
+ - Highest feasibility for potholes.
45
+ - Typical resolution and perspective supports pothole features.
46
+
47
+ - **Drone (good)**:
48
+ - Works well at low altitude with good GSD (cm/px).
49
+ - Requires flight plan and stable capture.
50
+
51
+ - **Satellite (usually not feasible)**:
52
+ - Most satellite imagery is too low resolution for potholes.
53
+ - Only very high-res (sub-10cm) commercial imagery could work, and still hard due to shadows and angle.
54
+
55
+ ## 4) Detection pipeline (integrated with current system)
56
+
57
+ Implemented integration strategy:
58
+
59
+ - Add **Pothole Detection** as another detection type in the menu.
60
+ - Route through the existing `POST /api/detect` using:
61
+ - `detection_type=pothole_detection`
62
+ - `pothole_model=<selected>`
63
+ - Separate engine module:
64
+ - `app/pothole_engine.py`
65
+
66
+ ### Starter logic implemented (Rule-Based v1)
67
+
68
+ For fast CPU MVP (vehicle/drone imagery):
69
+ - shadow/dark region score (local brightness drop)
70
+ - rough texture score (Laplacian roughness)
71
+ - edge score (Canny)
72
+ - fuse + sensitivity percentile threshold
73
+ - region extraction + severity/confidence
74
+
75
+ ## 5) Model architectures to implement next
76
+
77
+ - **YOLOv8n (boxes)** for fast detection and scalable deployment.
78
+ - **SegFormer-b0 / U-Net** for pixel-level damage mapping.
79
+ - Optional road ROI segmentation first to reduce false positives.
80
+
81
+ ## 6) Immediate next steps for dataset preprocessing / feature extraction
82
+
83
+ 1. Define input standard: camera height, FOV, resolution, and capture protocol.
84
+ 2. Build a labeled dataset:
85
+ - bounding boxes or masks for potholes,
86
+ - metadata: day/night, wet/dry, shadows.
87
+ 3. Add preprocessing:
88
+ - road ROI extraction,
89
+ - illumination normalization,
90
+ - motion blur handling.
91
+ 4. Train baseline YOLO model and integrate as `pothole_model=YOLOv8`.
92
+
README.md CHANGED
@@ -17,6 +17,7 @@ Standalone web application for satellite image change detection with **user acco
17
  - **Database** — SQLite (or set `DATABASE_URL` for PostgreSQL); stores users and detection runs
18
  - **Change detection** — Same model as the original app: AI-based, image difference, feature-based, hybrid
19
  - **Detection menu** — Choose between General Change Detection and Landslide Detection (Uttarakhand starter)
 
20
  - **Object classification** — Changed regions labeled as Water, Vegetation/Tree, Building, Road, Bare Ground/Soil
21
  - **History** — List of past runs with overlay images and stats
22
  - **UI** — Single-page app with a dark, “control room” style and teal accents
@@ -64,6 +65,11 @@ Standalone web application for satellite image change detection with **user acco
64
  - Dataset preprocessing starter: `app/landslide_preprocessing.py`
65
  - Planning/research brief: `Landslide_Detection_Uttarakhand_Integration_Plan.md`
66
 
 
 
 
 
 
67
  ## Project layout
68
 
69
  ```
 
17
  - **Database** — SQLite (or set `DATABASE_URL` for PostgreSQL); stores users and detection runs
18
  - **Change detection** — Same model as the original app: AI-based, image difference, feature-based, hybrid
19
  - **Detection menu** — Choose between General Change Detection and Landslide Detection (Uttarakhand starter)
20
+ - **Pothole detection** — Separate detection type for road damage (starter pipeline + future model hook)
21
  - **Object classification** — Changed regions labeled as Water, Vegetation/Tree, Building, Road, Bare Ground/Soil
22
  - **History** — List of past runs with overlay images and stats
23
  - **UI** — Single-page app with a dark, “control room” style and teal accents
 
65
  - Dataset preprocessing starter: `app/landslide_preprocessing.py`
66
  - Planning/research brief: `Landslide_Detection_Uttarakhand_Integration_Plan.md`
67
 
68
+ - **Pothole module**:
69
+ - Integrated at runtime through the same `/api/detect` endpoint using `detection_type=pothole_detection`.
70
+ - Engine code: `app/pothole_engine.py`
71
+ - Planning/research brief: `Pothole_Detection_Integration_Plan.md`
72
+
73
  ## Project layout
74
 
75
  ```
app/main.py CHANGED
@@ -227,6 +227,7 @@ async def detect(
227
  method: str = Form("AI-Based Deep Learning"),
228
  detection_type: str = Form("change_detection"),
229
  landslide_model: str = Form("Rule-Based v1"),
 
230
  title: str = Form("Untitled run"),
231
  zone: str = Form(""),
232
  village: str = Form(""),
@@ -278,6 +279,16 @@ async def detect(
278
  detection_sensitivity=detection_sensitivity,
279
  min_region_area=min_region_area,
280
  )
 
 
 
 
 
 
 
 
 
 
281
  else:
282
  detection_type = "change_detection"
283
  from .detection_engine import run_detection
@@ -576,3 +587,8 @@ def detect_change_page():
576
  @app.get("/detect/landslide", response_class=HTMLResponse)
577
  def detect_landslide_page():
578
  return index()
 
 
 
 
 
 
227
  method: str = Form("AI-Based Deep Learning"),
228
  detection_type: str = Form("change_detection"),
229
  landslide_model: str = Form("Rule-Based v1"),
230
+ pothole_model: str = Form("Rule-Based v1"),
231
  title: str = Form("Untitled run"),
232
  zone: str = Form(""),
233
  village: str = Form(""),
 
279
  detection_sensitivity=detection_sensitivity,
280
  min_region_area=min_region_area,
281
  )
282
+ elif detection_type == "pothole_detection":
283
+ from .pothole_engine import run_pothole_detection
284
+ method = f"Pothole - {pothole_model}"
285
+ change_mask, result_image, stats, change_regions = run_pothole_detection(
286
+ before_pil,
287
+ after_pil,
288
+ model_name=pothole_model,
289
+ detection_sensitivity=detection_sensitivity,
290
+ min_region_area=min_region_area,
291
+ )
292
  else:
293
  detection_type = "change_detection"
294
  from .detection_engine import run_detection
 
587
  @app.get("/detect/landslide", response_class=HTMLResponse)
588
  def detect_landslide_page():
589
  return index()
590
+
591
+
592
+ @app.get("/detect/pothole", response_class=HTMLResponse)
593
+ def detect_pothole_page():
594
+ return index()
app/pothole_engine.py ADDED
@@ -0,0 +1,191 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Pothole / road damage detection starter engine.
3
+
4
+ Goal: separate pipeline that can evolve to a real model (YOLO/Mask R-CNN/SegFormer).
5
+ This initial version is a CPU-friendly heuristic detector designed for vehicle/drone imagery.
6
+
7
+ Notes:
8
+ - Satellite imagery is generally too coarse for potholes unless very high resolution (<10 cm/px).
9
+ - Vehicle camera or low-altitude drone is the realistic input for pothole detection.
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import cv2
14
+ import numpy as np
15
+ from PIL import Image
16
+
17
+
18
+ def _preprocess(image: Image.Image, max_size: int = 1600) -> np.ndarray:
19
+ arr = np.array(image.convert("RGB"))
20
+ h, w = arr.shape[:2]
21
+ if max(h, w) > max_size:
22
+ s = max_size / max(h, w)
23
+ arr = cv2.resize(arr, (max(1, int(w * s)), max(1, int(h * s))), interpolation=cv2.INTER_AREA)
24
+ return arr
25
+
26
+
27
+ def _norm01(x: np.ndarray) -> np.ndarray:
28
+ x = x.astype(np.float32)
29
+ lo = float(np.min(x))
30
+ hi = float(np.max(x))
31
+ if hi - lo < 1e-8:
32
+ return np.zeros_like(x, dtype=np.float32)
33
+ return (x - lo) / (hi - lo)
34
+
35
+
36
+ def _road_texture_response(gray: np.ndarray) -> np.ndarray:
37
+ # Potholes often appear as dark regions with sharp boundaries + rough texture.
38
+ blur = cv2.GaussianBlur(gray, (5, 5), 0)
39
+ lap = cv2.Laplacian(blur, cv2.CV_32F, ksize=3)
40
+ rough = cv2.GaussianBlur(np.abs(lap), (7, 7), 0)
41
+ return _norm01(rough)
42
+
43
+
44
+ def _shadow_score(gray: np.ndarray) -> np.ndarray:
45
+ # Darker-than-local background regions.
46
+ local = cv2.GaussianBlur(gray, (31, 31), 0)
47
+ diff = np.clip((local - gray).astype(np.float32), 0, None)
48
+ return _norm01(diff)
49
+
50
+
51
+ def _edge_score(gray: np.ndarray) -> np.ndarray:
52
+ med = float(np.median(gray))
53
+ t1 = int(max(0, 0.66 * med))
54
+ t2 = int(min(255, 1.33 * med))
55
+ edges = cv2.Canny(gray, t1, t2)
56
+ edges = cv2.dilate(edges, np.ones((3, 3), np.uint8), iterations=1)
57
+ return edges.astype(np.float32) / 255.0
58
+
59
+
60
+ def _clean(mask: np.ndarray) -> np.ndarray:
61
+ m = mask.copy()
62
+ m = cv2.medianBlur(m, 5)
63
+ k_open = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
64
+ k_close = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9, 9))
65
+ m = cv2.morphologyEx(m, cv2.MORPH_OPEN, k_open)
66
+ m = cv2.morphologyEx(m, cv2.MORPH_CLOSE, k_close)
67
+ return m
68
+
69
+
70
+ def _extract_regions(mask: np.ndarray, min_area: int = 220):
71
+ n, labels, stats, cents = cv2.connectedComponentsWithStats(mask, connectivity=8)
72
+ h, w = mask.shape[:2]
73
+ img_area = h * w
74
+ regs = []
75
+ rid = 0
76
+ for i in range(1, n):
77
+ area = int(stats[i, cv2.CC_STAT_AREA])
78
+ if area < min_area:
79
+ continue
80
+ x = int(stats[i, cv2.CC_STAT_LEFT])
81
+ y = int(stats[i, cv2.CC_STAT_TOP])
82
+ bw = int(stats[i, cv2.CC_STAT_WIDTH])
83
+ bh = int(stats[i, cv2.CC_STAT_HEIGHT])
84
+ if bw * bh > img_area * 0.25:
85
+ continue
86
+ ar = max(bw, bh) / max(1, min(bw, bh))
87
+ if ar > 6.0:
88
+ continue
89
+ cx, cy = cents[i]
90
+ fill = area / max(1, bw * bh)
91
+ conf = float(np.clip(0.25 + fill * 0.7, 0.25, 0.95))
92
+ sev = "minor"
93
+ if area / img_area > 0.01:
94
+ sev = "major"
95
+ elif area / img_area > 0.003:
96
+ sev = "moderate"
97
+ rid += 1
98
+ regs.append(
99
+ {
100
+ "id": rid,
101
+ "area": area,
102
+ "bbox": (x, y, bw, bh),
103
+ "center": (int(cx), int(cy)),
104
+ "object_type": "Pothole / Road Damage",
105
+ "confidence": conf,
106
+ "severity": sev,
107
+ "sub_type": "Pothole",
108
+ "sub_type_confidence": conf,
109
+ "estimated_stories": None,
110
+ "estimated_height_m": None,
111
+ "construction_stage": None,
112
+ }
113
+ )
114
+ regs.sort(key=lambda r: r["area"], reverse=True)
115
+ return regs[:80]
116
+
117
+
118
+ def _visualize(img: np.ndarray, mask: np.ndarray, regions: list[dict]) -> np.ndarray:
119
+ out = img.copy().astype(np.float32)
120
+ m = (mask > 127).astype(np.float32)
121
+ # Orange overlay for road damage
122
+ layer = np.zeros_like(out)
123
+ layer[:, :, 0] = 255
124
+ layer[:, :, 1] = 165
125
+ alpha = 0.35
126
+ for c in range(3):
127
+ out[:, :, c] = out[:, :, c] * (1 - m * alpha) + layer[:, :, c] * (m * alpha)
128
+ vis = np.clip(out, 0, 255).astype(np.uint8)
129
+ for r in regions:
130
+ x, y, w, h = r["bbox"]
131
+ cv2.rectangle(vis, (x, y), (x + w, y + h), (0, 140, 255), 2)
132
+ cv2.putText(vis, str(r["id"]), (x + 4, max(14, y - 4)), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1, cv2.LINE_AA)
133
+ return vis
134
+
135
+
136
+ def run_pothole_detection(
137
+ before_pil: Image.Image,
138
+ after_pil: Image.Image,
139
+ model_name: str = "Rule-Based v1",
140
+ detection_sensitivity: float = 0.6,
141
+ min_region_area: int | None = None,
142
+ ):
143
+ """
144
+ Current UI uses (before, after) upload. For potholes, we treat the *after* image as
145
+ the road image and ignore the before image.
146
+ """
147
+ img = _preprocess(after_pil)
148
+ gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
149
+
150
+ rough = _road_texture_response(gray)
151
+ shadow = _shadow_score(gray)
152
+ edges = _edge_score(gray)
153
+
154
+ fused = 0.45 * shadow + 0.35 * rough + 0.20 * edges
155
+ fused = cv2.GaussianBlur(fused.astype(np.float32), (7, 7), 0)
156
+
157
+ sens = float(np.clip(detection_sensitivity, 0.0, 1.0))
158
+ q = float(np.clip(0.975 - (sens - 0.5) * 0.10, 0.85, 0.985))
159
+ thr = float(np.quantile(fused, q))
160
+ mask = (fused >= thr).astype(np.uint8) * 255
161
+ mask = _clean(mask)
162
+
163
+ if min_region_area is None:
164
+ min_region_area = int(max(150, min(1200, mask.shape[0] * mask.shape[1] * 0.00005)))
165
+ regions = _extract_regions(mask, min_area=int(min_region_area))
166
+ result = _visualize(img, mask, regions)
167
+
168
+ total = int(mask.shape[0] * mask.shape[1])
169
+ changed = int(np.sum(mask > 127))
170
+ stats = {
171
+ "total_pixels": total,
172
+ "changed_pixels": changed,
173
+ "unchanged_pixels": total - changed,
174
+ "change_percentage": (changed / total * 100.0) if total else 0.0,
175
+ "image_width": mask.shape[1],
176
+ "image_height": mask.shape[0],
177
+ "threshold_debug": {
178
+ "method": f"Pothole Detection ({model_name})",
179
+ "threshold_used": int(np.clip(thr * 255.0, 0, 255)),
180
+ "threshold_percentile_q": q,
181
+ "sensitivity": sens,
182
+ },
183
+ "params": {
184
+ "detection_sensitivity": sens,
185
+ "min_region_area": int(min_region_area),
186
+ "model_name": model_name,
187
+ "input": "after_only",
188
+ },
189
+ }
190
+ return mask, result, stats, regions
191
+
static/js/app.js CHANGED
@@ -15,6 +15,7 @@ function showView(id) {
15
  function getDetectionTypeFromPath() {
16
  const p = (window.location.pathname || '').toLowerCase();
17
  if (p.includes('/detect/landslide')) return 'landslide_detection';
 
18
  if (p.includes('/detect/change')) return 'change_detection';
19
  return null;
20
  }
@@ -27,7 +28,9 @@ function applyDetectionTypeToUI(type) {
27
  }
28
 
29
  function pathForDetectionType(type) {
30
- return type === 'landslide_detection' ? '/detect/landslide' : '/detect/change';
 
 
31
  }
32
 
33
  function navigateToDetectionType(type, replace = false) {
@@ -48,6 +51,9 @@ document.getElementById('btn-type-change')?.addEventListener('click', () => {
48
  document.getElementById('btn-type-landslide')?.addEventListener('click', () => {
49
  navigateToDetectionType('landslide_detection');
50
  });
 
 
 
51
 
52
  function showError(id, msg) {
53
  const el = document.getElementById(id);
@@ -232,10 +238,11 @@ function setupUploadZone(inputId, nameId, zoneId, previewId) {
232
  setupUploadZone('file-before', 'name-before', 'zone-before', 'preview-before');
233
  setupUploadZone('file-after', 'name-after', 'zone-after', 'preview-after');
234
 
235
- // ---- Detection menu (General vs Landslide) ----
236
  (function initDetectionMenu() {
237
  const typeSel = document.getElementById('detect-type');
238
  const landslideGroup = document.getElementById('landslide-model-group');
 
239
  const methodGroup = document.getElementById('detect-method')?.closest('.form-group');
240
  const regGroup = document.getElementById('detect-registration')?.closest('.form-group');
241
  const normGroup = document.getElementById('detect-normalization')?.closest('.form-group');
@@ -243,10 +250,13 @@ setupUploadZone('file-after', 'name-after', 'zone-after', 'preview-after');
243
 
244
  function refresh() {
245
  const isLandslide = typeSel.value === 'landslide_detection';
 
246
  if (landslideGroup) landslideGroup.classList.toggle('hidden', !isLandslide);
247
- if (methodGroup) methodGroup.classList.toggle('hidden', isLandslide);
248
- if (regGroup) regGroup.classList.toggle('hidden', isLandslide);
249
- if (normGroup) normGroup.classList.toggle('hidden', isLandslide);
 
 
250
  }
251
 
252
  typeSel.addEventListener('change', refresh);
@@ -483,6 +493,9 @@ document.getElementById('form-detect')?.addEventListener('submit', async (e) =>
483
  if (detectionType === 'landslide_detection') {
484
  form.append('landslide_model', document.getElementById('landslide-model')?.value || 'Rule-Based v1');
485
  }
 
 
 
486
  form.append('title', document.getElementById('detect-title').value || 'Untitled run');
487
  form.append('zone', document.getElementById('detect-zone').value || '');
488
  form.append('village', document.getElementById('detect-village').value || '');
 
15
  function getDetectionTypeFromPath() {
16
  const p = (window.location.pathname || '').toLowerCase();
17
  if (p.includes('/detect/landslide')) return 'landslide_detection';
18
+ if (p.includes('/detect/pothole')) return 'pothole_detection';
19
  if (p.includes('/detect/change')) return 'change_detection';
20
  return null;
21
  }
 
28
  }
29
 
30
  function pathForDetectionType(type) {
31
+ if (type === 'landslide_detection') return '/detect/landslide';
32
+ if (type === 'pothole_detection') return '/detect/pothole';
33
+ return '/detect/change';
34
  }
35
 
36
  function navigateToDetectionType(type, replace = false) {
 
51
  document.getElementById('btn-type-landslide')?.addEventListener('click', () => {
52
  navigateToDetectionType('landslide_detection');
53
  });
54
+ document.getElementById('btn-type-pothole')?.addEventListener('click', () => {
55
+ navigateToDetectionType('pothole_detection');
56
+ });
57
 
58
  function showError(id, msg) {
59
  const el = document.getElementById(id);
 
238
  setupUploadZone('file-before', 'name-before', 'zone-before', 'preview-before');
239
  setupUploadZone('file-after', 'name-after', 'zone-after', 'preview-after');
240
 
241
+ // ---- Detection menu (General vs Landslide vs Pothole) ----
242
  (function initDetectionMenu() {
243
  const typeSel = document.getElementById('detect-type');
244
  const landslideGroup = document.getElementById('landslide-model-group');
245
+ const potholeGroup = document.getElementById('pothole-model-group');
246
  const methodGroup = document.getElementById('detect-method')?.closest('.form-group');
247
  const regGroup = document.getElementById('detect-registration')?.closest('.form-group');
248
  const normGroup = document.getElementById('detect-normalization')?.closest('.form-group');
 
250
 
251
  function refresh() {
252
  const isLandslide = typeSel.value === 'landslide_detection';
253
+ const isPothole = typeSel.value === 'pothole_detection';
254
  if (landslideGroup) landslideGroup.classList.toggle('hidden', !isLandslide);
255
+ if (potholeGroup) potholeGroup.classList.toggle('hidden', !isPothole);
256
+ const hideCore = isLandslide || isPothole;
257
+ if (methodGroup) methodGroup.classList.toggle('hidden', hideCore);
258
+ if (regGroup) regGroup.classList.toggle('hidden', hideCore);
259
+ if (normGroup) normGroup.classList.toggle('hidden', hideCore);
260
  }
261
 
262
  typeSel.addEventListener('change', refresh);
 
493
  if (detectionType === 'landslide_detection') {
494
  form.append('landslide_model', document.getElementById('landslide-model')?.value || 'Rule-Based v1');
495
  }
496
+ if (detectionType === 'pothole_detection') {
497
+ form.append('pothole_model', document.getElementById('pothole-model')?.value || 'Rule-Based v1');
498
+ }
499
  form.append('title', document.getElementById('detect-title').value || 'Untitled run');
500
  form.append('zone', document.getElementById('detect-zone').value || '');
501
  form.append('village', document.getElementById('detect-village').value || '');
templates/index.html CHANGED
@@ -144,6 +144,8 @@
144
  <button type="button" class="btn btn-primary btn-block" id="btn-type-change">General Change Detection</button>
145
  <div style="height:0.75rem;"></div>
146
  <button type="button" class="btn btn-secondary btn-block" id="btn-type-landslide">Landslide Detection (Uttarakhand)</button>
 
 
147
  </div>
148
  </div>
149
  </section>
@@ -181,6 +183,7 @@
181
  <select id="detect-type">
182
  <option value="change_detection" selected>General Change Detection</option>
183
  <option value="landslide_detection">Landslide Detection (Uttarakhand)</option>
 
184
  </select>
185
  </div>
186
  <div class="form-group hidden" id="landslide-model-group">
@@ -189,6 +192,12 @@
189
  <option value="Rule-Based v1" selected>Rule-Based v1 (Vegetation Loss + Soil Gain)</option>
190
  </select>
191
  </div>
 
 
 
 
 
 
192
  </div>
193
  <div class="location-row">
194
  <div class="form-group">
@@ -392,6 +401,6 @@
392
  </div>
393
  </div>
394
 
395
- <script src="/static/js/app.js?v=30"></script>
396
  </body>
397
  </html>
 
144
  <button type="button" class="btn btn-primary btn-block" id="btn-type-change">General Change Detection</button>
145
  <div style="height:0.75rem;"></div>
146
  <button type="button" class="btn btn-secondary btn-block" id="btn-type-landslide">Landslide Detection (Uttarakhand)</button>
147
+ <div style="height:0.75rem;"></div>
148
+ <button type="button" class="btn btn-secondary btn-block" id="btn-type-pothole">Pothole Detection</button>
149
  </div>
150
  </div>
151
  </section>
 
183
  <select id="detect-type">
184
  <option value="change_detection" selected>General Change Detection</option>
185
  <option value="landslide_detection">Landslide Detection (Uttarakhand)</option>
186
+ <option value="pothole_detection">Pothole Detection</option>
187
  </select>
188
  </div>
189
  <div class="form-group hidden" id="landslide-model-group">
 
192
  <option value="Rule-Based v1" selected>Rule-Based v1 (Vegetation Loss + Soil Gain)</option>
193
  </select>
194
  </div>
195
+ <div class="form-group hidden" id="pothole-model-group">
196
+ <label for="pothole-model">Pothole Model</label>
197
+ <select id="pothole-model">
198
+ <option value="Rule-Based v1" selected>Rule-Based v1 (Edges + Shadows + Texture)</option>
199
+ </select>
200
+ </div>
201
  </div>
202
  <div class="location-row">
203
  <div class="form-group">
 
401
  </div>
402
  </div>
403
 
404
+ <script src="/static/js/app.js?v=31"></script>
405
  </body>
406
  </html>