coderuday21 commited on
Commit
92ca429
Β·
1 Parent(s): 630a171

Add Delhi zone/village dropdowns, location tracking, and project documentation

Browse files
AI_Change_Detection_Handover.md ADDED
@@ -0,0 +1,280 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # AI Change Detection β€” Project Handover Document
2
+
3
+ **Prepared by:** Development Team
4
+ **Date:** February 7, 2026
5
+ **Version:** 2.0
6
+ **Live URL:** [Hugging Face Spaces β€” AI Change Detection](https://huggingface.co/spaces/coderuday21/satdetect)
7
+
8
+ ---
9
+
10
+ ## 1. Executive Summary
11
+
12
+ AI Change Detection is a web-based application that automatically detects, classifies, and visualizes changes between two satellite or aerial images taken at different times. Users upload a "before" and "after" image, and the system identifies what changed on the ground β€” new buildings, deforestation, road construction, demolitions, water body changes, and more.
13
+
14
+ The application provides detailed reports including change statistics, object-level classification with sub-type breakdowns, 3D building analysis (estimated height and construction stage), and an interactive visual overlay highlighting all detected changes in red.
15
+
16
+ ---
17
+
18
+ ## 2. Problem Statement
19
+
20
+ Manually comparing satellite imagery to identify ground-level changes is:
21
+
22
+ - **Time-consuming** β€” Analysts spend hours visually scanning large image pairs pixel by pixel.
23
+ - **Error-prone** β€” Human eyes miss subtle changes in vegetation health, gradual construction, or surface texture shifts.
24
+ - **Inconsistent** β€” Different analysts may classify the same change differently.
25
+ - **Unscalable** β€” As the volume of imagery grows (urban planning, environmental monitoring, disaster response), manual review cannot keep pace.
26
+
27
+ **AI Change Detection solves this** by automating the entire detection-to-classification pipeline, delivering consistent, quantified results in seconds.
28
+
29
+ ---
30
+
31
+ ## 3. Target Users
32
+
33
+ | User Role | How They Use It |
34
+ |-----------|----------------|
35
+ | Urban planners | Track unauthorized construction, building expansion, and infrastructure changes |
36
+ | Environmental analysts | Monitor deforestation, vegetation health decline, and land-use changes |
37
+ | Disaster response teams | Assess structural damage after natural disasters by comparing pre/post imagery |
38
+ | Agricultural analysts | Detect crop changes, seasonal variation, and land conversion |
39
+ | Government agencies | Monitor compliance with zoning and environmental regulations |
40
+ | Construction firms | Track construction progress and stage classification over time |
41
+
42
+ ---
43
+
44
+ ## 4. Key Features
45
+
46
+ ### 4.1 Multi-Method Change Detection
47
+
48
+ The system offers four detection methods, each suited to different scenarios:
49
+
50
+ | Method | Best For | Speed |
51
+ |--------|----------|-------|
52
+ | **Image Difference** | Quick scans where changes involve obvious color shifts (e.g., cleared forest) | Fastest |
53
+ | **Feature-Based** | Subtle, distributed changes across large areas (e.g., gradual land-use shift) | Fast |
54
+ | **AI-Based Deep Learning** | Highest accuracy β€” combines color, structure, texture, and edge analysis | Moderate |
55
+ | **Hybrid Approach** | Maximum coverage when unsure which type of changes to expect | Slowest |
56
+
57
+ Users select the method per run depending on their accuracy vs. speed needs.
58
+
59
+ ### 4.2 Intelligent Pre-Processing
60
+
61
+ Before comparing images, the system can automatically:
62
+
63
+ - **Align images** (Image Registration) β€” Corrects for camera angle, zoom, and position differences between the two photos so the comparison is pixel-accurate.
64
+ - **Normalize lighting** (Radiometric Normalization) β€” Corrects for brightness/contrast differences caused by weather, time of day, or different cameras, preventing false positives from lighting alone.
65
+
66
+ Both options are user-toggleable per run.
67
+
68
+ ### 4.3 Ground-Level Change Classification
69
+
70
+ Every detected change region is automatically classified into one of six categories:
71
+
72
+ | Category | What It Detects |
73
+ |----------|----------------|
74
+ | **New Construction / Building** | New structures appearing on previously empty land |
75
+ | **Demolition / Clearing** | Structures or vegetation that have been removed |
76
+ | **Vegetation Change** | Any change in plant cover β€” forests, crops, parks |
77
+ | **Water Body Change** | New, expanded, or reduced water features |
78
+ | **Road / Pavement Change** | New roads, widened paths, resurfaced areas |
79
+ | **Bare Land / Soil Change** | Exposed earth, grading, land preparation |
80
+
81
+ **Transient objects are automatically filtered out** β€” people, vehicles, animals, and shadows are excluded so only permanent ground-level changes are reported.
82
+
83
+ ### 4.4 Detailed Sub-Classification
84
+
85
+ Each primary category is further broken down into specific sub-types by comparing the "before" and "after" regions:
86
+
87
+ **Vegetation Sub-Types:**
88
+
89
+ | Sub-Type | What It Means |
90
+ |----------|---------------|
91
+ | Deforestation / Tree Removal | Green area has been cleared β€” trees or vegetation removed |
92
+ | New Vegetation / Growth | Previously bare area now has plant cover |
93
+ | Crop / Agricultural Change | Farmland has changed crop type or land use pattern |
94
+ | Vegetation Health Decline | Plants are browning or showing signs of drought/disease |
95
+ | Seasonal Variation | Normal seasonal color shift, both periods still green |
96
+
97
+ **Structural Sub-Types:**
98
+
99
+ | Sub-Type | What It Means |
100
+ |----------|---------------|
101
+ | New Building | Structure appeared where none existed before |
102
+ | Building Expansion | Existing building has been extended or enlarged |
103
+ | Renovation / Modification | Building exists in both images but has changed appearance |
104
+ | Partial Demolition | Part of a structure has been removed |
105
+ | Full Demolition | Entire structure has been removed, area is now bare |
106
+ | Infrastructure Change | Non-building structures (bridges, towers, utilities) |
107
+
108
+ **Road Sub-Types:**
109
+
110
+ | Sub-Type | What It Means |
111
+ |----------|---------------|
112
+ | New Road / Pavement | Road appeared where none existed before |
113
+ | Road Widening | Existing road has been expanded |
114
+ | Road Resurfacing | Road surface has been repaved (different appearance, same width) |
115
+ | Road Deterioration | Road surface has degraded |
116
+
117
+ ### 4.5 3D Building Analysis
118
+
119
+ For regions classified as buildings or construction, the system provides:
120
+
121
+ - **Estimated Stories** β€” Number of floors, calculated from shadow analysis and building footprint geometry.
122
+ - **Estimated Height** β€” Height in meters (based on 3m per story).
123
+ - **Construction Stage** β€” Classified as Foundation, Structural, Under Construction, or Complete based on visual features.
124
+
125
+ ### 4.6 Interactive Visual Output
126
+
127
+ - **Red overlay** β€” All detected changes are highlighted in red on the "after" image.
128
+ - **Before/After slider** β€” A draggable comparison slider lets users visually inspect what changed by sliding between the original and the annotated result.
129
+ - **Annotated bounding boxes** β€” Each detected region gets a labeled bounding box showing its sub-type, stories, and construction stage.
130
+
131
+ ### 4.7 User Accounts & History
132
+
133
+ - **Secure login system** β€” Email/password registration with encrypted password storage.
134
+ - **Detection history** β€” All past runs are saved with their results, overlay images, and metadata. Users can revisit or delete previous runs.
135
+ - **Password reset** β€” Users can reset their password from the login screen.
136
+
137
+ ### 4.8 Responsive Design
138
+
139
+ The application is fully functional on desktop browsers and mobile devices, with an optimized layout for smaller screens.
140
+
141
+ ---
142
+
143
+ ## 5. Technology Stack
144
+
145
+ | Layer | Technology | Purpose |
146
+ |-------|-----------|---------|
147
+ | **Frontend** | HTML, CSS, JavaScript (Vanilla) | Single-page application β€” no framework dependencies |
148
+ | **Backend** | Python, FastAPI | REST API server handling authentication and detection |
149
+ | **Image Processing** | OpenCV, NumPy, Scikit-learn, Pillow | Core algorithms for detection, classification, and visualization |
150
+ | **Database** | SQLite (local), PostgreSQL (production-ready) | User accounts and detection run history |
151
+ | **Authentication** | JWT tokens, bcrypt password hashing | Secure stateless authentication |
152
+ | **Deployment** | Docker, Hugging Face Spaces | Containerized cloud deployment |
153
+
154
+ ---
155
+
156
+ ## 6. Application Screens
157
+
158
+ ### Login / Registration
159
+ - Clean login form with email and password
160
+ - Password visibility toggle (eye icon)
161
+ - "Forgot password" option
162
+ - Toggle between sign-in and registration
163
+
164
+ ### Dashboard (Main Screen)
165
+ - **Upload Section** β€” Two drag-and-drop zones for before and after images with live previews
166
+ - **Configuration** β€” Method selector, run title, and optional pre-processing toggles
167
+ - **Results Section** β€” Appears after running detection:
168
+ - Statistics bar: change percentage, changed pixels, total pixels, region count
169
+ - Before/after comparison slider
170
+ - Detailed table of all detected regions with type, sub-type, confidence, area, building data, and coordinates
171
+ - **History Section** β€” List of all past runs with option to view overlay or delete
172
+
173
+ ---
174
+
175
+ ## 7. Output Data Per Detection Run
176
+
177
+ Each run produces the following data:
178
+
179
+ | Field | Description |
180
+ |-------|-------------|
181
+ | Change Percentage | Proportion of the image area that changed |
182
+ | Changed Pixels | Total number of pixels detected as changed |
183
+ | Total Pixels | Total image area in pixels |
184
+ | Regions Count | Number of distinct change regions identified |
185
+ | **Per Region:** | |
186
+ | Change Type | Primary category (Construction, Vegetation, etc.) |
187
+ | Sub-Type | Detailed sub-classification (New Building, Deforestation, etc.) |
188
+ | Confidence | Classification confidence score (0–100%) |
189
+ | Area | Region size in pixels |
190
+ | Estimated Stories | Number of building floors (building regions only) |
191
+ | Estimated Height | Height in meters (building regions only) |
192
+ | Construction Stage | Foundation / Structural / Under Construction / Complete (building regions only) |
193
+ | Center Coordinates | Location of the region center in the image |
194
+ | Overlay Image | Annotated image with red highlights and bounding boxes |
195
+
196
+ ---
197
+
198
+ ## 8. Deployment & Access
199
+
200
+ ### Current Deployment
201
+ - **Platform:** Hugging Face Spaces (free tier)
202
+ - **URL:** https://huggingface.co/spaces/coderuday21/satdetect
203
+ - **Container:** Docker-based, auto-deploys on code push
204
+ - **Database:** SQLite (ephemeral β€” resets when the container restarts)
205
+
206
+ ### Limitations of Current Deployment
207
+ | Limitation | Impact | Mitigation Path |
208
+ |-----------|--------|-----------------|
209
+ | Ephemeral database | User accounts and history are lost on container restart | Migrate to external PostgreSQL database |
210
+ | No email verification on password reset | Anyone who knows an email can reset the password | Implement token-based email verification flow |
211
+ | Single-worker server | Cannot handle high concurrent load | Scale to multiple workers with PostgreSQL |
212
+ | Free-tier resources | Limited CPU/memory, images capped at 2000px and 20MB | Upgrade to paid hosting for larger images |
213
+
214
+ ### For Production Deployment
215
+ To move from the current demo to a production environment, the following would be needed:
216
+ 1. External PostgreSQL database for persistent storage
217
+ 2. Email service integration (SendGrid, AWS SES) for password reset verification
218
+ 3. Paid cloud hosting (AWS, GCP, or Azure) for better performance
219
+ 4. HTTPS with a custom domain
220
+ 5. Environment-based secret key management
221
+
222
+ ---
223
+
224
+ ## 9. How Accuracy Works
225
+
226
+ The system does **not** use a pre-trained deep learning model (no neural network). Instead, it uses a **multi-signal fusion approach** β€” combining multiple independent analysis methods that each measure a different aspect of change:
227
+
228
+ 1. **Color difference** β€” Measures how much the color changed at each pixel (in perceptually uniform color space).
229
+ 2. **Structural similarity** β€” Measures whether the patterns and structures at each pixel changed.
230
+ 3. **Texture analysis** β€” Measures whether the surface texture changed (smooth β†’ rough, etc.).
231
+ 4. **Edge detection** β€” Measures whether structural boundaries appeared or disappeared.
232
+
233
+ These four signals are weighted by their discriminative power and fused into a single change map. This approach is:
234
+
235
+ - **Transparent** β€” Every decision can be traced to specific visual features (no "black box").
236
+ - **Deterministic** β€” The same inputs always produce the same outputs.
237
+ - **No training data required** β€” Works on any satellite/aerial imagery without needing labeled datasets.
238
+
239
+ **Trade-off:** A trained deep learning model (e.g., U-Net, Siamese network) would likely achieve higher accuracy on specific datasets, but would require labeled training data and GPU resources. The current approach provides good accuracy out-of-the-box for general-purpose change detection.
240
+
241
+ ---
242
+
243
+ ## 10. Future Enhancement Opportunities
244
+
245
+ | Enhancement | Business Value |
246
+ |-------------|---------------|
247
+ | Deep learning model integration (U-Net / Siamese CNN) | Significantly higher detection accuracy on trained domains |
248
+ | Batch processing / multi-image upload | Analyze time-series imagery for trend analysis |
249
+ | GIS integration (GeoTIFF, coordinate mapping) | Pin changes to real-world geographic coordinates |
250
+ | PDF/Excel report export | Downloadable reports for stakeholder distribution |
251
+ | Webhook / API integration | Automate detection as part of larger monitoring pipelines |
252
+ | Role-based access (admin, analyst, viewer) | Team collaboration with permission controls |
253
+ | Notification system | Alert stakeholders when significant changes are detected |
254
+ | Cloud-optimized image storage | Handle very large satellite tiles (10,000+ pixels) |
255
+
256
+ ---
257
+
258
+ ## 11. Repository Structure
259
+
260
+ ```
261
+ change_detection_webapp/
262
+ β”œβ”€β”€ app/
263
+ β”‚ β”œβ”€β”€ main.py β†’ API server and route definitions
264
+ β”‚ β”œβ”€β”€ auth.py β†’ Authentication (JWT, password hashing)
265
+ β”‚ β”œβ”€β”€ models.py β†’ Database table definitions
266
+ β”‚ β”œβ”€β”€ database.py β†’ Database connection setup
267
+ β”‚ └── detection_engine.py β†’ All image processing and classification algorithms
268
+ β”œβ”€β”€ templates/
269
+ β”‚ └── index.html β†’ Frontend page structure
270
+ β”œβ”€β”€ static/
271
+ β”‚ β”œβ”€β”€ css/style.css β†’ All visual styling
272
+ β”‚ └── js/app.js β†’ Client-side logic
273
+ β”œβ”€β”€ Dockerfile β†’ Container build instructions
274
+ β”œβ”€β”€ requirements.txt β†’ Python package dependencies
275
+ └── ARCHITECTURE.md β†’ Technical architecture reference (for developers)
276
+ ```
277
+
278
+ ---
279
+
280
+ *For technical implementation details, refer to `ARCHITECTURE.md` in the project repository.*
ARCHITECTURE.md ADDED
@@ -0,0 +1,386 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # AI Change Detection β€” Architecture & Component Guide
2
+
3
+ ## Overview
4
+
5
+ AI Change Detection is a full-stack web application that detects, classifies, and visualizes changes between two satellite or aerial images (before and after). It uses multi-signal image processing β€” combining color analysis, structural similarity, texture descriptors, and edge detection β€” to identify ground-level changes such as new buildings, deforestation, road construction, and more.
6
+
7
+ ---
8
+
9
+ ## System Architecture
10
+
11
+ ```
12
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
13
+ β”‚ Browser (SPA) β”‚
14
+ β”‚ index.html + style.css + app.js β”‚
15
+ β”‚ Login / Register / Upload / Results / History β”‚
16
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
17
+ β”‚ REST API (JSON + multipart)
18
+ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
19
+ β”‚ FastAPI Backend (main.py) β”‚
20
+ β”‚ Auth routes Β· Detection route Β· History β”‚
21
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
22
+ β”‚ auth.py β”‚ models.pyβ”‚ detection_engine.py β”‚
23
+ β”‚ JWT auth β”‚ ORM β”‚ Image processing pipeline β”‚
24
+ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚
25
+ β”‚ database.py β”‚ SQLite (local/HF Spaces) β”‚
26
+ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
27
+ ```
28
+
29
+ ---
30
+
31
+ ## File-by-File Component Breakdown
32
+
33
+ ### 1. `app/main.py` β€” API Server & Route Definitions
34
+
35
+ **Role:** The central FastAPI application. Defines all HTTP endpoints, wires together authentication, database, and the detection engine.
36
+
37
+ **Use cases solved:**
38
+ - **User registration & login** (`POST /api/auth/register`, `POST /api/auth/login`) β€” Creates accounts and issues JWT tokens stored as HTTP-only cookies.
39
+ - **Session management** (`POST /api/auth/logout`, `GET /api/me`) β€” Clears auth cookies; returns the current authenticated user.
40
+ - **Password reset** (`POST /api/auth/reset-password`) β€” Allows users to reset their password by email (no email verification β€” documented limitation).
41
+ - **Change detection** (`POST /api/detect`) β€” Accepts two images (before/after) plus configuration options, runs the full detection pipeline, saves the overlay image to disk, stores run metadata in the database, and returns results with a base64-encoded overlay for immediate display.
42
+ - **History** (`GET /api/history`, `DELETE /api/history/{id}`) β€” Lists past detection runs for the logged-in user; allows deletion of individual runs and their overlay files.
43
+ - **Static file serving** β€” Mounts the `static/` directory for CSS/JS and serves `templates/index.html` as the SPA entry point.
44
+ - **Upload size limiting** β€” Rejects images larger than 20 MB to prevent out-of-memory crashes.
45
+
46
+ ---
47
+
48
+ ### 2. `app/auth.py` β€” Authentication & Authorization
49
+
50
+ **Role:** Handles password hashing, JWT token creation/validation, and user lookup.
51
+
52
+ **Use cases solved:**
53
+ - **Secure password storage** β€” Uses bcrypt via `passlib` to hash passwords before storing them. Plaintext passwords never touch the database.
54
+ - **Stateless authentication** β€” Issues HS256 JWT tokens with a 7-day expiry. Tokens are sent as HTTP-only cookies (for browser requests) and also accepted as Bearer tokens in the Authorization header.
55
+ - **Multi-source token resolution** β€” `get_current_user()` checks three sources in order: Authorization header, HTTP cookie, then form field. This ensures authentication works for both API clients and browser multipart form submissions.
56
+
57
+ ---
58
+
59
+ ### 3. `app/models.py` β€” Database Models (ORM)
60
+
61
+ **Role:** Defines the SQLAlchemy ORM models that map to database tables.
62
+
63
+ **Tables:**
64
+ - **`users`** β€” Stores account data: email, bcrypt-hashed password, full name, and creation timestamp. Each user has a one-to-many relationship with detection runs.
65
+ - **`detection_runs`** β€” Stores metadata for each detection run: title, method used, pixel statistics (total, changed, percentage), region count, path to the saved overlay image, and a JSON blob of all classified regions with their sub-types and 3D analysis data.
66
+
67
+ **Use case solved:** Persistent storage of user accounts and detection history so users can revisit past results.
68
+
69
+ ---
70
+
71
+ ### 4. `app/database.py` β€” Database Connection & Session Management
72
+
73
+ **Role:** Configures the SQLAlchemy engine, session factory, and data directory paths.
74
+
75
+ **Use cases solved:**
76
+ - **Environment-aware storage** β€” Detects Hugging Face Spaces (via `SPACE_ID` env var) and uses a writable home directory; falls back to a local `data/` directory for development.
77
+ - **Database portability** β€” Defaults to SQLite for zero-config local development. Supports PostgreSQL via `DATABASE_URL` environment variable for production (with automatic `postgres://` β†’ `postgresql://` rewrite for SQLAlchemy 2.x compatibility).
78
+ - **Session lifecycle** β€” The `get_db()` generator provides request-scoped database sessions with automatic cleanup.
79
+
80
+ ---
81
+
82
+ ### 5. `app/detection_engine.py` β€” Image Processing & Classification Pipeline
83
+
84
+ This is the core of the application. It contains all image analysis algorithms organized into a sequential pipeline.
85
+
86
+ #### 5a. Pre-processing (`preprocess_image`)
87
+
88
+ **Use case:** Normalizes input images to a consistent format before analysis.
89
+ - Converts grayscale or RGBA images to RGB.
90
+ - Downscales images larger than 2000px on any side to prevent memory issues while preserving enough detail for analysis.
91
+
92
+ #### 5b. Image Registration (`register_images`)
93
+
94
+ **Use case:** Aligns the "after" image to the "before" image so pixel-level comparison is meaningful.
95
+ - Uses ORB feature detection to find matching keypoints between both images.
96
+ - Applies Lowe's ratio test to filter spurious matches.
97
+ - Computes a RANSAC homography to warp the after image into alignment.
98
+ - Rejects bad alignments via inlier ratio threshold and homography determinant check (catches degenerate transforms).
99
+ - **Why it matters:** Without alignment, even a slight camera angle difference would cause the entire image to appear "changed."
100
+
101
+ #### 5c. Radiometric Normalization (`normalize_radiometry`)
102
+
103
+ **Use case:** Corrects brightness and contrast differences between the two images caused by different lighting conditions, seasons, or sensors.
104
+ - Performs histogram matching in LAB color space (perceptually uniform) so the "after" image has the same color distribution as the "before."
105
+ - Applies CLAHE (Contrast Limited Adaptive Histogram Equalization) symmetrically to both images to enhance local contrast without introducing bias.
106
+ - **Why it matters:** A cloudy day vs. a sunny day would otherwise create massive false-positive "changes" everywhere.
107
+
108
+ #### 5d. SSIM Change Map (`compute_ssim_change_map`)
109
+
110
+ **Use case:** Measures structural dissimilarity between corresponding regions of both images.
111
+ - Computes per-pixel Structural Similarity Index (SSIM) using Gaussian-weighted windows.
112
+ - Outputs a dissimilarity map where 0 = identical and 1 = completely different.
113
+ - Clamps variance values to prevent numerical instability from floating-point rounding.
114
+ - **Why it matters:** SSIM captures structural patterns (edges, textures) that simple color difference would miss.
115
+
116
+ #### 5e. Texture Features (`compute_lbp`, `compute_texture_change`)
117
+
118
+ **Use case:** Detects changes in surface texture (e.g., smooth road vs. rough construction rubble).
119
+ - Computes Local Binary Patterns (LBP) β€” a texture descriptor that encodes the relationship between each pixel and its neighbors.
120
+ - The absolute difference between before/after LBP maps highlights regions where surface texture changed.
121
+ - **Why it matters:** Two regions can have similar colors but very different textures (e.g., grass vs. artificial turf, bare soil vs. concrete).
122
+
123
+ #### 5f. Edge Change Detection (`compute_edge_change`)
124
+
125
+ **Use case:** Identifies new or removed structural boundaries.
126
+ - Uses Canny edge detection with adaptive thresholds based on median intensity.
127
+ - Dilates edges so nearby edges match (tolerates minor misalignment).
128
+ - Computes the absolute difference to find edges present in one image but not the other.
129
+ - **Why it matters:** New buildings, roads, and demolished structures introduce or remove strong edges.
130
+
131
+ #### 5g. Detection Methods (4 options)
132
+
133
+ | Method | Description | Best For |
134
+ |--------|-------------|----------|
135
+ | **Image Difference** | LAB color space Delta-E difference with Otsu thresholding | Fast, simple comparisons with clear color changes |
136
+ | **Feature-Based** | Multi-space (LAB + HSV) feature clustering via KMeans | Subtle, distributed changes across large areas |
137
+ | **AI-Based Deep Learning** | Multi-signal fusion (color + SSIM + texture + edge) with entropy-weighted adaptive combination | Highest accuracy; catches both color and structural changes |
138
+ | **Hybrid Approach** | Weighted blend of all three methods (20% difference + 30% feature + 50% AI) | Maximum coverage when unsure which method is best |
139
+
140
+ **Image Difference** (`image_difference_method`):
141
+ - Converts to LAB and computes a weighted Delta-E (CIE76) difference per pixel.
142
+ - Applies Otsu's automatic threshold to separate "changed" from "unchanged."
143
+ - Fast but only catches color changes β€” misses structural changes where color stays similar.
144
+
145
+ **Feature-Based** (`feature_based_method`):
146
+ - Extracts LAB and HSV difference features per pixel.
147
+ - Downsamples to 250K pixels for KMeans clustering (4 clusters), then maps labels back to full resolution.
148
+ - The cluster with the highest mean feature magnitude is labeled "change."
149
+ - Good at finding spatially distributed or gradual changes.
150
+
151
+ **AI-Based Deep Learning** (`ai_deep_learning_method`):
152
+ - Fuses four independent signals: multi-scale LAB color difference, SSIM structural dissimilarity, LBP texture change, and Canny edge change.
153
+ - Weights each channel by its information entropy (channels with more discriminative power get higher weight).
154
+ - Applies Otsu thresholding followed by bilateral filtering for edge-preserving smoothing.
155
+ - The most accurate method β€” captures color, structure, texture, and edge changes simultaneously.
156
+
157
+ **Hybrid** (`hybrid_method`):
158
+ - Runs all three methods and combines their masks with fixed weights.
159
+ - AI method gets the most weight (50%) as it's most reliable.
160
+
161
+ #### 5h. Post-processing (`_clean_mask`)
162
+
163
+ **Use case:** Cleans up raw change masks to remove noise and fill gaps.
164
+ - Morphological closing (fills small gaps inside detected regions).
165
+ - Morphological opening (removes small noise speckles).
166
+ - Contour-based hole filling (ensures detected regions are solid).
167
+ - Sensitivity parameter controls the aggressiveness of gap-filling.
168
+
169
+ #### 5i. Visualization (`visualize_changes`)
170
+
171
+ **Use case:** Creates the final overlay image showing detected changes.
172
+ - Draws a semi-transparent red overlay on all changed pixels in the "after" image.
173
+ - Draws white bounding boxes around each classified region.
174
+ - Annotates regions with labels showing sub-type, building stories, and construction stage.
175
+
176
+ #### 5j. Object Classification (`classify_object_type`, `classify_with_ensemble`)
177
+
178
+ **Use case:** Determines what kind of ground-level change each detected region represents.
179
+
180
+ **Feature extraction** (`extract_advanced_features`):
181
+ - Extracts 17 features per region: color stats (RGB, HSV, LAB means/stds), vegetation index (NDVI), texture (LBP variance, standard deviation), edges (density, orientation entropy), GLCM-like contrast, shape ratios, and color ratios.
182
+
183
+ **Transient object filtering** (`_is_transient_object`):
184
+ - Filters out non-permanent changes: people, vehicles, animals, shadows.
185
+ - Uses area, aspect ratio, edge density, and texture variance thresholds.
186
+
187
+ **Classification categories:**
188
+ | Category | Key Indicators |
189
+ |----------|---------------|
190
+ | New Construction/Building | Low orientation entropy, compact shape, moderate edges, low saturation |
191
+ | Demolition/Clearing | High texture variance, high entropy, bright, low vegetation |
192
+ | Vegetation Change | High NDVI, green ratio, medium texture, high saturation |
193
+ | Water Body Change | High blue ratio, low texture, smooth surface, specific hue range |
194
+ | Road/Pavement Change | Elongated shape, homogeneous color, low saturation, low entropy |
195
+ | Bare Land/Soil Change | High red ratio, low NDVI, low texture, earth-tone hue |
196
+
197
+ **Ensemble voting** (`classify_with_ensemble`):
198
+ - Classifies the full region plus 5 sub-regions (quadrants + center).
199
+ - Combines votes weighted by confidence.
200
+ - Boosts confidence when β‰₯60% of sub-regions agree.
201
+ - Only excludes a region as transient if a majority of sub-regions are flagged.
202
+
203
+ #### 5k. Vegetation Sub-Classification (`classify_vegetation_subtype`)
204
+
205
+ **Use case:** Provides detailed breakdown of vegetation changes by comparing the before and after region crops.
206
+
207
+ | Sub-Type | Detection Logic |
208
+ |----------|----------------|
209
+ | Deforestation/Tree Removal | Before was green (high NDVI), after is bare (NDVI dropped, brightness increased) |
210
+ | New Vegetation/Growth | Before was bare, after is green (NDVI increased, saturation up) |
211
+ | Crop/Agricultural Change | Regular texture patterns in either image, moderate NDVI shift, large area |
212
+ | Vegetation Health Decline | Both images green but NDVI and saturation slightly decreased (browning/drought) |
213
+ | Seasonal Variation | Mild color/texture shift, both images still green |
214
+
215
+ #### 5l. Structural Sub-Classification (`classify_structural_subtype`)
216
+
217
+ **Use case:** Provides detailed breakdown of structural and road changes by comparing before/after structure presence, edge density, and texture.
218
+
219
+ | Sub-Type | Detection Logic |
220
+ |----------|----------------|
221
+ | New Building | No structure before, structure detected after |
222
+ | Building Expansion | Structure in both images, but after has higher edge density |
223
+ | Renovation/Modification | Structure in both, similar density but different appearance (brightness/saturation shift) |
224
+ | Partial Demolition | Structure before, reduced edges after |
225
+ | Full Demolition | Structure before, bare/empty after |
226
+ | Infrastructure Change | Elongated shape with high geometric regularity |
227
+ | New Road/Pavement | No structure before, structure after (for road-type regions) |
228
+ | Road Widening | Structure in both, edge density increased |
229
+ | Road Resurfacing | Structure in both, similar edges but brightness changed |
230
+ | Road Deterioration | Texture increased, edges decreased (surface degradation) |
231
+
232
+ #### 5m. 3D Building Analysis
233
+
234
+ **Use case:** Estimates building height/stories and classifies construction stage for building-type regions.
235
+
236
+ **Shadow-based height estimation** (`estimate_building_height`):
237
+ - Detects new shadow pixels adjacent to the building by comparing brightness in the expanded bounding box area between before and after images.
238
+ - Measures shadow length as the maximum spatial extent of the shadow cluster.
239
+ - Combines shadow ratio, footprint geometry (compact = likely taller), and texture regularity to estimate story count.
240
+ - Assumes 3 meters per story.
241
+
242
+ **Construction stage classification** (`classify_construction_stage`):
243
+ | Stage | Visual Indicators |
244
+ |-------|------------------|
245
+ | Foundation | Low texture, low edges, homogeneous, soil/concrete colors |
246
+ | Structural | High edges, geometric regularity (low entropy), high GLCM contrast |
247
+ | Under Construction | Mixed materials, irregular texture, medium edge density |
248
+ | Complete | Uniform roof, clean edges, low entropy, consistent color |
249
+
250
+ ---
251
+
252
+ ### 6. `templates/index.html` β€” Frontend Structure
253
+
254
+ **Role:** Single-page application (SPA) with four views managed by JavaScript.
255
+
256
+ **Views:**
257
+ - **Login** β€” Email/password form with password visibility toggle and "Forgot password" link.
258
+ - **Register** β€” Account creation form with name, email, and password.
259
+ - **Forgot Password** β€” Email + new password form (simplified reset without email verification).
260
+ - **Dashboard** β€” The main workspace containing:
261
+ - **Upload zone** β€” Drag-and-drop or click-to-browse for before/after images with live previews.
262
+ - **Detection options** β€” Method selector, title input, image registration and radiometric normalization toggles.
263
+ - **Results panel** β€” Statistics boxes (change %, changed pixels, total pixels, region count), before/after comparison slider, and a detailed regions table with columns for change type, sub-type, confidence, area, stories, height, construction stage, and center coordinates.
264
+ - **History** β€” List of past detection runs with view and delete actions.
265
+
266
+ ---
267
+
268
+ ### 7. `static/css/style.css` β€” Visual Design
269
+
270
+ **Role:** Complete styling for the application using CSS custom properties and a gradient theme.
271
+
272
+ **Key design decisions:**
273
+ - **Color theme** β€” Linear gradient from `#2e33c5` (blue) to `#cf2040` (red) applied to the navbar, footer, buttons, and accent elements. White background for content areas.
274
+ - **Sticky navbar** β€” Full-viewport-width gradient bar that stays fixed at the top during scrolling.
275
+ - **Mobile responsive** β€” Grid layouts collapse to single columns on screens under 768px. Font sizes, padding, and table layouts adapt to small screens.
276
+ - **Glassmorphism dropdown** β€” User account menu uses a gradient background with subtle shadow for the avatar popup.
277
+ - **Compare slider** β€” Custom CSS for the before/after image comparison handle with gradient accents.
278
+ - **Stat boxes** β€” Responsive font sizing with `clamp()` and compact number formatting to prevent overflow.
279
+
280
+ ---
281
+
282
+ ### 8. `static/js/app.js` β€” Client-Side Logic
283
+
284
+ **Role:** All browser-side interactivity β€” authentication flow, file uploads, API communication, result rendering, and UI state management.
285
+
286
+ **Key responsibilities:**
287
+ - **Authentication** β€” Login, register, forgot-password form handlers that call the API and store JWT tokens in localStorage. Automatic session check on page load (`init()`).
288
+ - **File upload** β€” Drag-and-drop and click-to-browse handlers with live image previews.
289
+ - **Detection submission** β€” Builds a FormData payload with images and settings, sends to `/api/detect`, and renders results.
290
+ - **Result rendering** β€” Populates stat boxes with compact-formatted numbers, fills the regions table, sets up the before/after comparison slider, and scrolls to results.
291
+ - **History management** β€” Loads past runs, renders them with view/delete actions, and handles deletion with an animated confirmation modal.
292
+ - **UI utilities** β€” View switching, error/success alerts, avatar dropdown toggle, password visibility toggle, and HTML escaping.
293
+
294
+ ---
295
+
296
+ ### 9. `Dockerfile` β€” Container Configuration
297
+
298
+ **Role:** Defines the Docker image for deployment on Hugging Face Spaces.
299
+
300
+ **Key aspects:**
301
+ - Base image: `python:3.11-slim` with OpenCV system dependencies (`libgl1`, `libglib2.0-0`, etc.).
302
+ - Non-root user (`appuser`) as required by Hugging Face Spaces security policy.
303
+ - Runs Gunicorn with a single Uvicorn worker on port 7860 (HF Spaces default) with 120-second timeout for long detection runs.
304
+ - Single worker prevents SQLite write-lock race conditions.
305
+
306
+ ---
307
+
308
+ ### 10. `requirements.txt` β€” Python Dependencies
309
+
310
+ | Package | Purpose |
311
+ |---------|---------|
312
+ | `fastapi` | Web framework for the REST API |
313
+ | `uvicorn` | ASGI server (production via Gunicorn) |
314
+ | `gunicorn` | Process manager for production deployment |
315
+ | `python-multipart` | Handles multipart/form-data file uploads |
316
+ | `sqlalchemy` | ORM for database operations |
317
+ | `psycopg2-binary` | PostgreSQL driver (for production databases) |
318
+ | `python-jose` | JWT token encoding/decoding |
319
+ | `passlib` + `bcrypt` | Password hashing (bcrypt pinned to 4.0.1 for compatibility) |
320
+ | `pillow` | Image loading and format conversion |
321
+ | `numpy` | Numerical array operations for image processing |
322
+ | `opencv-python-headless` | Core image processing (registration, morphology, edge detection, color conversion) |
323
+ | `scikit-learn` | KMeans clustering for feature-based detection; StandardScaler for normalization |
324
+ | `scipy` | Scientific computing utilities |
325
+
326
+ ---
327
+
328
+ ## Data Flow: End-to-End Detection Run
329
+
330
+ ```
331
+ 1. User uploads before + after images via browser
332
+ β”‚
333
+ 2. POST /api/detect (multipart form with images + settings)
334
+ β”‚
335
+ 3. Authentication check (JWT from cookie/header/form)
336
+ β”‚
337
+ 4. Pre-processing
338
+ β”œβ”€β”€ Convert to RGB, limit to 2000px
339
+ β”‚
340
+ 5. Image Registration (optional)
341
+ β”œβ”€β”€ ORB keypoints β†’ ratio test β†’ RANSAC homography β†’ warp
342
+ β”‚
343
+ 6. Radiometric Normalization (optional)
344
+ β”œβ”€β”€ LAB histogram matching + symmetric CLAHE
345
+ β”‚
346
+ 7. Change Detection (selected method)
347
+ β”œβ”€β”€ Image Difference: LAB Delta-E + Otsu
348
+ β”œβ”€β”€ Feature-Based: LAB/HSV features + KMeans clustering
349
+ β”œβ”€β”€ AI-Based: Multi-signal fusion (color + SSIM + texture + edge)
350
+ └── Hybrid: Weighted combination of all three
351
+ β”‚
352
+ 8. Post-processing
353
+ β”œβ”€β”€ Morphological closing/opening + hole filling
354
+ β”‚
355
+ 9. Region Analysis
356
+ β”œβ”€β”€ Connected components β†’ bounding boxes
357
+ β”œβ”€β”€ Feature extraction (17 features per region)
358
+ β”œβ”€β”€ Transient object filtering (remove people, cars, etc.)
359
+ β”œβ”€β”€ Primary classification (6 ground-level categories)
360
+ β”œβ”€β”€ Ensemble voting (full region + 5 sub-regions)
361
+ β”‚
362
+ 10. Sub-Classification
363
+ β”œβ”€β”€ Vegetation: Deforestation / Growth / Crop / Health / Seasonal
364
+ β”œβ”€β”€ Structural: New / Expansion / Renovation / Partial Demo / Full Demo / Infrastructure
365
+ └── Road: New / Widening / Resurfacing / Deterioration
366
+ β”‚
367
+ 11. 3D Building Analysis (for construction/demolition regions)
368
+ β”œβ”€β”€ Shadow detection β†’ height/stories estimation
369
+ └── Construction stage classification
370
+ β”‚
371
+ 12. Visualization
372
+ β”œβ”€β”€ Red overlay on changed pixels
373
+ β”œβ”€β”€ Bounding boxes + annotation labels
374
+ β”‚
375
+ 13. Response
376
+ β”œβ”€β”€ Save overlay to disk + database record
377
+ └── Return JSON: stats, regions, base64 overlay image
378
+ ```
379
+
380
+ ---
381
+
382
+ ## Deployment
383
+
384
+ - **Local development:** Run `uvicorn app.main:app --reload` from the project root. SQLite database is created in `data/satellite_app.db`.
385
+ - **Hugging Face Spaces:** Push to the linked HF repo. The Dockerfile builds and deploys automatically. Database is stored at `/home/appuser/data/` (ephemeral β€” resets on container restart).
386
+ - **Production with PostgreSQL:** Set `DATABASE_URL` environment variable to a PostgreSQL connection string. Set `SECRET_KEY` to a strong random value.
app/main.py CHANGED
@@ -7,6 +7,7 @@ from pathlib import Path
7
  from typing import Optional
8
 
9
  import numpy as np
 
10
  from fastapi import FastAPI, Depends, File, Form, HTTPException, Request, UploadFile
11
  from fastapi.responses import FileResponse, HTMLResponse, JSONResponse
12
  from fastapi.staticfiles import StaticFiles
@@ -29,7 +30,18 @@ from .detection_engine import run_detection
29
 
30
  Base.metadata.create_all(bind=engine, checkfirst=True)
31
 
32
- app = FastAPI(title="Satellite Change Detection", version="1.0.0")
 
 
 
 
 
 
 
 
 
 
 
33
 
34
  # Mount static files
35
  STATIC_DIR = Path(__file__).resolve().parent.parent / "static"
@@ -159,6 +171,8 @@ async def detect(
159
  after: UploadFile = File(...),
160
  method: str = Form("AI-Based Deep Learning"),
161
  title: str = Form("Untitled run"),
 
 
162
  enable_registration: bool = Form(True),
163
  enable_normalization: bool = Form(True),
164
  access_token: Optional[str] = Form(None),
@@ -221,6 +235,8 @@ async def detect(
221
  user_id=user.id,
222
  title=title,
223
  method=method,
 
 
224
  total_pixels=total_px,
225
  changed_pixels=changed_px,
226
  change_percentage=change_pct,
@@ -240,6 +256,8 @@ async def detect(
240
  "id": run.id,
241
  "title": run.title,
242
  "method": run.method,
 
 
243
  "statistics": {
244
  "totalPixels": total_px,
245
  "changedPixels": changed_px,
@@ -281,6 +299,8 @@ def history(
281
  "id": r.id,
282
  "title": r.title,
283
  "method": r.method,
 
 
284
  "changePercentage": r.change_percentage,
285
  "regionsCount": r.regions_count,
286
  "overlayUrl": f"/api/overlay/{r.overlay_path}" if r.overlay_path else None,
 
7
  from typing import Optional
8
 
9
  import numpy as np
10
+ from sqlalchemy import text as sa_text
11
  from fastapi import FastAPI, Depends, File, Form, HTTPException, Request, UploadFile
12
  from fastapi.responses import FileResponse, HTMLResponse, JSONResponse
13
  from fastapi.staticfiles import StaticFiles
 
30
 
31
  Base.metadata.create_all(bind=engine, checkfirst=True)
32
 
33
+ # Lightweight migration: add columns introduced after initial schema
34
+ with engine.connect() as conn:
35
+ for col, col_type in [("zone", "VARCHAR(128) DEFAULT ''"),
36
+ ("village", "VARCHAR(128) DEFAULT ''")]:
37
+ try:
38
+ conn.execute(sa_text(
39
+ f"ALTER TABLE detection_runs ADD COLUMN {col} {col_type}"))
40
+ conn.commit()
41
+ except Exception:
42
+ conn.rollback()
43
+
44
+ app = FastAPI(title="AI Change Detection", version="2.0.0")
45
 
46
  # Mount static files
47
  STATIC_DIR = Path(__file__).resolve().parent.parent / "static"
 
171
  after: UploadFile = File(...),
172
  method: str = Form("AI-Based Deep Learning"),
173
  title: str = Form("Untitled run"),
174
+ zone: str = Form(""),
175
+ village: str = Form(""),
176
  enable_registration: bool = Form(True),
177
  enable_normalization: bool = Form(True),
178
  access_token: Optional[str] = Form(None),
 
235
  user_id=user.id,
236
  title=title,
237
  method=method,
238
+ zone=zone,
239
+ village=village,
240
  total_pixels=total_px,
241
  changed_pixels=changed_px,
242
  change_percentage=change_pct,
 
256
  "id": run.id,
257
  "title": run.title,
258
  "method": run.method,
259
+ "zone": run.zone or "",
260
+ "village": run.village or "",
261
  "statistics": {
262
  "totalPixels": total_px,
263
  "changedPixels": changed_px,
 
299
  "id": r.id,
300
  "title": r.title,
301
  "method": r.method,
302
+ "zone": r.zone or "",
303
+ "village": r.village or "",
304
  "changePercentage": r.change_percentage,
305
  "regionsCount": r.regions_count,
306
  "overlayUrl": f"/api/overlay/{r.overlay_path}" if r.overlay_path else None,
app/models.py CHANGED
@@ -29,6 +29,8 @@ class DetectionRun(Base):
29
  change_percentage = Column(Float, nullable=False)
30
  regions_count = Column(Integer, default=0)
31
  overlay_path = Column(String(512), default="") # optional: path to saved overlay image
 
 
32
  regions_json = Column(Text, default="[]") # JSON list of regions
33
  created_at = Column(DateTime, default=datetime.utcnow)
34
 
 
29
  change_percentage = Column(Float, nullable=False)
30
  regions_count = Column(Integer, default=0)
31
  overlay_path = Column(String(512), default="") # optional: path to saved overlay image
32
+ zone = Column(String(128), default="")
33
+ village = Column(String(128), default="")
34
  regions_json = Column(Text, default="[]") # JSON list of regions
35
  created_at = Column(DateTime, default=datetime.utcnow)
36
 
static/css/style.css CHANGED
@@ -448,6 +448,29 @@ input:focus, select:focus, textarea:focus {
448
  font-size: 0.9rem;
449
  }
450
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
  /* ---- Upload zones ---- */
452
  .upload-grid {
453
  display: grid;
@@ -562,6 +585,15 @@ input:focus, select:focus, textarea:focus {
562
  letter-spacing: 0.06em;
563
  margin-top: 0.2rem;
564
  }
 
 
 
 
 
 
 
 
 
565
 
566
  /* ---- Compare slider ---- */
567
  .compare-slider {
@@ -729,6 +761,12 @@ input:focus, select:focus, textarea:focus {
729
  color: var(--text-muted);
730
  margin-right: 0.3rem;
731
  }
 
 
 
 
 
 
732
 
733
  .history-actions {
734
  display: flex;
 
448
  font-size: 0.9rem;
449
  }
450
 
451
+ /* ---- Location row ---- */
452
+ .location-row {
453
+ display: grid;
454
+ grid-template-columns: 1fr 1fr;
455
+ gap: 1rem;
456
+ margin-bottom: 1.25rem;
457
+ }
458
+ .location-row label {
459
+ display: flex;
460
+ align-items: center;
461
+ gap: 0.35rem;
462
+ }
463
+ .location-row label svg {
464
+ color: var(--grad-start);
465
+ flex-shrink: 0;
466
+ }
467
+ .location-row select {
468
+ width: 100%;
469
+ }
470
+ @media (max-width: 640px) {
471
+ .location-row { grid-template-columns: 1fr; }
472
+ }
473
+
474
  /* ---- Upload zones ---- */
475
  .upload-grid {
476
  display: grid;
 
585
  letter-spacing: 0.06em;
586
  margin-top: 0.2rem;
587
  }
588
+ .stat-box-wide {
589
+ grid-column: span 2;
590
+ }
591
+ .stat-box .value-sm {
592
+ font-size: clamp(0.75rem, 3vw, 1.05rem);
593
+ }
594
+ @media (max-width: 640px) {
595
+ .stat-box-wide { grid-column: span 1; }
596
+ }
597
 
598
  /* ---- Compare slider ---- */
599
  .compare-slider {
 
761
  color: var(--text-muted);
762
  margin-right: 0.3rem;
763
  }
764
+ .history-meta .tag-loc {
765
+ background: linear-gradient(135deg, var(--grad-start), var(--grad-end));
766
+ color: #fff;
767
+ border-color: transparent;
768
+ font-family: var(--font);
769
+ }
770
 
771
  .history-actions {
772
  display: flex;
static/js/app.js CHANGED
@@ -182,6 +182,97 @@ function setupUploadZone(inputId, nameId, zoneId, previewId) {
182
  setupUploadZone('file-before', 'name-before', 'zone-before', 'preview-before');
183
  setupUploadZone('file-after', 'name-after', 'zone-after', 'preview-after');
184
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  // ---- Run detection ----
186
  document.getElementById('form-detect')?.addEventListener('submit', async (e) => {
187
  e.preventDefault();
@@ -204,6 +295,8 @@ document.getElementById('form-detect')?.addEventListener('submit', async (e) =>
204
  form.append('after', after);
205
  form.append('method', document.getElementById('detect-method').value);
206
  form.append('title', document.getElementById('detect-title').value || 'Untitled run');
 
 
207
  form.append('enable_registration', document.getElementById('detect-registration').checked);
208
  form.append('enable_normalization', document.getElementById('detect-normalization').checked);
209
  if (token) form.append('access_token', token);
@@ -247,11 +340,15 @@ function showResult(data) {
247
  const statsEl = document.getElementById('result-stats');
248
  const tbody = document.getElementById('regions-tbody');
249
 
 
 
 
250
  statsEl.innerHTML = `
251
  <div class="stat-box"><div class="value">${data.statistics.changePercentage.toFixed(2)}%</div><div class="label">Changed</div></div>
252
  <div class="stat-box"><div class="value" title="${data.statistics.changedPixels.toLocaleString()}">${formatCompact(data.statistics.changedPixels)}</div><div class="label">Changed px</div></div>
253
  <div class="stat-box"><div class="value" title="${data.statistics.totalPixels.toLocaleString()}">${formatCompact(data.statistics.totalPixels)}</div><div class="label">Total px</div></div>
254
  <div class="stat-box"><div class="value">${(data.regions || []).length}</div><div class="label">Regions</div></div>
 
255
  `;
256
 
257
  const beforeImg = document.getElementById('compare-before-img');
@@ -331,12 +428,15 @@ async function loadHistory() {
331
  list.innerHTML = '<div class="history-empty">No detection runs yet. Upload images above to get started.</div>';
332
  return;
333
  }
334
- list.innerHTML = items.map((r) => `
 
 
335
  <div class="history-item" data-id="${r.id}">
336
  <div class="history-info">
337
  <div class="history-title">${escapeHtml(r.title)}</div>
338
  <div class="history-meta">
339
  <span class="tag">${r.method}</span>
 
340
  ${r.changePercentage.toFixed(2)}% changed &middot; ${r.regionsCount} regions &middot; ${formatDate(r.createdAt)}
341
  </div>
342
  </div>
@@ -346,8 +446,8 @@ async function loadHistory() {
346
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>
347
  </button>
348
  </div>
349
- </div>
350
- `).join('');
351
  } catch (_) {
352
  list.innerHTML = '<div class="history-empty">Could not load history.</div>';
353
  }
 
182
  setupUploadZone('file-before', 'name-before', 'zone-before', 'preview-before');
183
  setupUploadZone('file-after', 'name-after', 'zone-after', 'preview-after');
184
 
185
+ // ---- Delhi Zone β†’ Village cascading dropdowns ----
186
+ const DELHI_ZONES = {
187
+ "Central Delhi": [
188
+ "Karol Bagh", "Paharganj", "Daryaganj", "Rajinder Nagar", "Patel Nagar",
189
+ "Anand Parbat", "Bapa Nagar", "Prasad Nagar", "Dev Nagar", "Old Rajinder Nagar"
190
+ ],
191
+ "New Delhi": [
192
+ "Connaught Place", "Chanakyapuri", "Lodhi Road", "Mandi House",
193
+ "India Gate", "Khan Market", "Barakhamba", "Gole Market", "Sansad Marg"
194
+ ],
195
+ "North Delhi": [
196
+ "Civil Lines", "Model Town", "Sadar Bazaar", "Timarpur", "Gulabi Bagh",
197
+ "Kamla Nagar", "Shakti Nagar", "Roop Nagar", "Vijay Nagar", "Mukherjee Nagar",
198
+ "GTB Nagar", "Adarsh Nagar", "Azadpur", "Wazirabad"
199
+ ],
200
+ "North West Delhi": [
201
+ "Rohini", "Narela", "Bawana", "Alipur", "Shalimar Bagh",
202
+ "Pitampura", "Kanjhawala", "Mundka", "Sultanpuri", "Mangolpuri",
203
+ "Begumpur", "Pooth Kalan", "Holambi Kalan", "Bankner", "Siraspur"
204
+ ],
205
+ "North East Delhi": [
206
+ "Seelampur", "Jafrabad", "Mustafabad", "Babarpur", "Gokulpuri",
207
+ "Yamuna Vihar", "Karawal Nagar", "Dayalpur", "Khajuri Khas",
208
+ "Bhajanpura", "Harsh Vihar", "Brahmpuri", "Ghonda"
209
+ ],
210
+ "East Delhi": [
211
+ "Preet Vihar", "Laxmi Nagar", "Mayur Vihar Phase I", "Mayur Vihar Phase II",
212
+ "Mayur Vihar Phase III", "Patparganj", "Pandav Nagar", "Shakarpur",
213
+ "Mandawali", "Kalyanpuri", "Trilokpuri", "Kondli", "Gharoli",
214
+ "Khichripur", "Anand Vihar"
215
+ ],
216
+ "Shahdara": [
217
+ "Shahdara", "Vivek Vihar", "Dilshad Garden", "Seema Puri", "New Seelampur",
218
+ "Nand Nagri", "Harsh Vihar", "Jhilmil Colony", "Mansarovar Park"
219
+ ],
220
+ "South Delhi": [
221
+ "Hauz Khas", "Mehrauli", "Saket", "Kalkaji", "Greater Kailash",
222
+ "Malviya Nagar", "Vasant Kunj", "Chattarpur", "Lado Sarai",
223
+ "Fatehpur Beri", "Mandi Village", "Dera Village", "Aaya Nagar",
224
+ "Sultanpur", "Ghitorni", "Satbari", "Jonapur", "Asola"
225
+ ],
226
+ "South East Delhi": [
227
+ "Defence Colony", "Okhla", "Jamia Nagar", "Badarpur", "Jaitpur",
228
+ "Madanpur Khadar", "Sarita Vihar", "Jasola", "Sukhdev Vihar",
229
+ "Tughlakabad", "Sangam Vihar", "Mithapur", "Pul Pehlad"
230
+ ],
231
+ "South West Delhi": [
232
+ "Dwarka", "Najafgarh", "Kapashera", "Palam", "Dabri",
233
+ "Mahavir Enclave", "Bindapur", "Uttam Nagar", "Nasirpur",
234
+ "Chhawla", "Dichaon Kalan", "Ghumanhera", "Jhatikara",
235
+ "Rawta", "Pochanpur", "Bijwasan", "Sarangpur", "Paprawat"
236
+ ],
237
+ "West Delhi": [
238
+ "Rajouri Garden", "Janakpuri", "Tilak Nagar", "Vikaspuri",
239
+ "Hari Nagar", "Subhash Nagar", "Tagore Garden", "Moti Nagar",
240
+ "Kirti Nagar", "Punjabi Bagh", "Nangloi Jat", "Nilothi",
241
+ "Mundka", "Madipur", "Paschim Vihar"
242
+ ]
243
+ };
244
+
245
+ (function initZoneVillage() {
246
+ const zoneSel = document.getElementById('detect-zone');
247
+ const villageSel = document.getElementById('detect-village');
248
+ if (!zoneSel || !villageSel) return;
249
+
250
+ Object.keys(DELHI_ZONES).sort().forEach(z => {
251
+ const opt = document.createElement('option');
252
+ opt.value = z;
253
+ opt.textContent = z;
254
+ zoneSel.appendChild(opt);
255
+ });
256
+
257
+ zoneSel.addEventListener('change', () => {
258
+ const zone = zoneSel.value;
259
+ villageSel.innerHTML = '';
260
+ if (!zone) {
261
+ villageSel.disabled = true;
262
+ villageSel.innerHTML = '<option value="">β€” Select Zone first β€”</option>';
263
+ return;
264
+ }
265
+ villageSel.disabled = false;
266
+ villageSel.innerHTML = '<option value="">β€” Select Village / Area β€”</option>';
267
+ (DELHI_ZONES[zone] || []).forEach(v => {
268
+ const opt = document.createElement('option');
269
+ opt.value = v;
270
+ opt.textContent = v;
271
+ villageSel.appendChild(opt);
272
+ });
273
+ });
274
+ })();
275
+
276
  // ---- Run detection ----
277
  document.getElementById('form-detect')?.addEventListener('submit', async (e) => {
278
  e.preventDefault();
 
295
  form.append('after', after);
296
  form.append('method', document.getElementById('detect-method').value);
297
  form.append('title', document.getElementById('detect-title').value || 'Untitled run');
298
+ form.append('zone', document.getElementById('detect-zone').value || '');
299
+ form.append('village', document.getElementById('detect-village').value || '');
300
  form.append('enable_registration', document.getElementById('detect-registration').checked);
301
  form.append('enable_normalization', document.getElementById('detect-normalization').checked);
302
  if (token) form.append('access_token', token);
 
340
  const statsEl = document.getElementById('result-stats');
341
  const tbody = document.getElementById('regions-tbody');
342
 
343
+ const locParts = [data.village, data.zone].filter(Boolean);
344
+ const locLabel = locParts.length ? locParts.join(', ') : 'β€”';
345
+
346
  statsEl.innerHTML = `
347
  <div class="stat-box"><div class="value">${data.statistics.changePercentage.toFixed(2)}%</div><div class="label">Changed</div></div>
348
  <div class="stat-box"><div class="value" title="${data.statistics.changedPixels.toLocaleString()}">${formatCompact(data.statistics.changedPixels)}</div><div class="label">Changed px</div></div>
349
  <div class="stat-box"><div class="value" title="${data.statistics.totalPixels.toLocaleString()}">${formatCompact(data.statistics.totalPixels)}</div><div class="label">Total px</div></div>
350
  <div class="stat-box"><div class="value">${(data.regions || []).length}</div><div class="label">Regions</div></div>
351
+ <div class="stat-box stat-box-wide"><div class="value value-sm" title="${locLabel}">${locLabel}</div><div class="label">Location</div></div>
352
  `;
353
 
354
  const beforeImg = document.getElementById('compare-before-img');
 
428
  list.innerHTML = '<div class="history-empty">No detection runs yet. Upload images above to get started.</div>';
429
  return;
430
  }
431
+ list.innerHTML = items.map((r) => {
432
+ const loc = [r.village, r.zone].filter(Boolean).join(', ');
433
+ return `
434
  <div class="history-item" data-id="${r.id}">
435
  <div class="history-info">
436
  <div class="history-title">${escapeHtml(r.title)}</div>
437
  <div class="history-meta">
438
  <span class="tag">${r.method}</span>
439
+ ${loc ? `<span class="tag tag-loc">${escapeHtml(loc)}</span>` : ''}
440
  ${r.changePercentage.toFixed(2)}% changed &middot; ${r.regionsCount} regions &middot; ${formatDate(r.createdAt)}
441
  </div>
442
  </div>
 
446
  <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"/><line x1="10" y1="11" x2="10" y2="17"/><line x1="14" y1="11" x2="14" y2="17"/></svg>
447
  </button>
448
  </div>
449
+ </div>`;
450
+ }).join('');
451
  } catch (_) {
452
  list.innerHTML = '<div class="history-empty">Could not load history.</div>';
453
  }
templates/index.html CHANGED
@@ -167,6 +167,26 @@
167
  <h3>New Detection Run</h3>
168
  </div>
169
  <form id="form-detect">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  <div class="upload-grid">
171
  <div class="upload-zone" id="zone-before">
172
  <input type="file" id="file-before" accept="image/png,image/jpeg,image/jpg" />
@@ -292,6 +312,6 @@
292
  </div>
293
  </div>
294
 
295
- <script src="/static/js/app.js?v=12"></script>
296
  </body>
297
  </html>
 
167
  <h3>New Detection Run</h3>
168
  </div>
169
  <form id="form-detect">
170
+ <div class="location-row">
171
+ <div class="form-group">
172
+ <label for="detect-zone">
173
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z"/><circle cx="12" cy="10" r="3"/></svg>
174
+ Zone
175
+ </label>
176
+ <select id="detect-zone">
177
+ <option value="">β€” Select Zone β€”</option>
178
+ </select>
179
+ </div>
180
+ <div class="form-group">
181
+ <label for="detect-village">
182
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
183
+ Village / Area
184
+ </label>
185
+ <select id="detect-village" disabled>
186
+ <option value="">β€” Select Zone first β€”</option>
187
+ </select>
188
+ </div>
189
+ </div>
190
  <div class="upload-grid">
191
  <div class="upload-zone" id="zone-before">
192
  <input type="file" id="file-before" accept="image/png,image/jpeg,image/jpg" />
 
312
  </div>
313
  </div>
314
 
315
+ <script src="/static/js/app.js?v=13"></script>
316
  </body>
317
  </html>