TurkishCodeMan commited on
Commit
369aca7
·
verified ·
1 Parent(s): 775bf75

Update MapTrace Gradio demo

Browse files
Files changed (4) hide show
  1. README.md +12 -6
  2. app.py +245 -0
  3. requirements.txt +7 -0
  4. upload_space.py +56 -0
README.md CHANGED
@@ -1,12 +1,18 @@
1
  ---
2
  title: MapTrace Path Planner
3
- emoji: 🐨
4
- colorFrom: red
5
- colorTo: pink
6
  sdk: gradio
7
- sdk_version: 6.8.0
8
  app_file: app.py
9
- pinned: false
 
 
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
1
  ---
2
  title: MapTrace Path Planner
3
+ emoji: 🗺️
4
+ colorFrom: green
5
+ colorTo: blue
6
  sdk: gradio
7
+ sdk_version: "5.0.0"
8
  app_file: app.py
9
+ pinned: true
10
+ license: apache-2.0
11
+ short_description: Visual path planning on maps with Qwen3.5-0.8B
12
  ---
13
 
14
+ # MapTrace Path Planner
15
+
16
+ Qwen3.5-0.8B with Partial Fine-Tuning on google/MapTrace dataset.
17
+
18
+ Upload a map image, enter start (green) and end (red) coordinates in normalized [0,1] space, and see the predicted traversable path overlaid on the image.
app.py ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Qwen3.5-0.8B MapTrace - Gradio Demo
4
+ Predicts a traversable path between start (green) and end (red) locations on a map image.
5
+ """
6
+
7
+ import re
8
+ import ast
9
+ import math
10
+ import torch
11
+ import gradio as gr
12
+ import numpy as np
13
+ from PIL import Image, ImageDraw, ImageFont
14
+ from transformers import AutoModelForImageTextToText, AutoProcessor
15
+
16
+ # ── CONFIG ────────────────────────────────────────────────────────────────────
17
+ MODEL_ID = "TurkishCodeMan/Qwen3.5-0.8B-MapTrace-PartialFT" # HF repo adın
18
+
19
+ # ── MODEL YÜKLEME (Uygulama başlatılınca bir kez) ─────────────────────────────
20
+ print(f"Loading model: {MODEL_ID}")
21
+ processor = AutoProcessor.from_pretrained(MODEL_ID, trust_remote_code=True)
22
+ model = AutoModelForImageTextToText.from_pretrained(
23
+ MODEL_ID,
24
+ torch_dtype=torch.bfloat16,
25
+ device_map="auto",
26
+ trust_remote_code=True,
27
+ )
28
+ model.eval()
29
+ print("✅ Model loaded and ready!")
30
+
31
+
32
+ # ── YARDIMCI FONKSİYONLAR ────────────────────────────────────────────────────
33
+
34
+ def parse_coordinates(text: str) -> list[tuple[float, float]]:
35
+ """Model çıktısından koordinatları ayıklar."""
36
+ # Pattern 1: [(x, y), ...] formatı
37
+ coords = re.findall(r"\((-?\d+\.?\d*),\s*(-?\d+\.?\d*)\)", text)
38
+ if coords:
39
+ return [(float(x), float(y)) for x, y in coords]
40
+ return []
41
+
42
+
43
+ def draw_path_on_image(
44
+ image: Image.Image,
45
+ path_coords: list[tuple[float, float]],
46
+ start_xy: tuple[float, float],
47
+ end_xy: tuple[float, float],
48
+ ) -> Image.Image:
49
+ """
50
+ Harita görselinin üzerine tahmin edilen yolu çizer.
51
+ - Yol: Kalın sarı çizgi + turuncu noktalar
52
+ - Start: Parlak yeşil daire
53
+ - End : Parlak kırmızı daire
54
+ """
55
+ img = image.copy().convert("RGBA")
56
+ overlay = Image.new("RGBA", img.size, (0, 0, 0, 0))
57
+ draw = ImageDraw.Draw(overlay)
58
+
59
+ W, H = img.size
60
+
61
+ def norm_to_px(x, y):
62
+ return int(x * W), int(y * H)
63
+
64
+ # Yol Çizimi
65
+ if len(path_coords) >= 2:
66
+ pixel_path = [norm_to_px(*p) for p in path_coords]
67
+
68
+ # Yol gölgesi (siyah, kalın)
69
+ draw.line(pixel_path, fill=(0, 0, 0, 160), width=7)
70
+ # Ana yol (sarı)
71
+ draw.line(pixel_path, fill=(255, 215, 0, 230), width=4)
72
+
73
+ # Ara noktalar
74
+ for px, py in pixel_path[1:-1]:
75
+ r = 5
76
+ draw.ellipse([(px - r, py - r), (px + r, py + r)], fill=(255, 140, 0, 255))
77
+
78
+ # Başlangıç noktası (yeşil)
79
+ sx, sy = norm_to_px(*start_xy)
80
+ draw.ellipse([(sx - 10, sy - 10), (sx + 10, sy + 10)], fill=(0, 200, 80, 255), outline=(255, 255, 255, 255), width=2)
81
+
82
+ # Bitiş noktası (kırmızı)
83
+ ex, ey = norm_to_px(*end_xy)
84
+ draw.ellipse([(ex - 10, ey - 10), (ex + 10, ey + 10)], fill=(220, 40, 40, 255), outline=(255, 255, 255, 255), width=2)
85
+
86
+ # Composite
87
+ result = Image.alpha_composite(img, overlay).convert("RGB")
88
+ return result
89
+
90
+
91
+ def run_inference(image: Image.Image, start_x: float, start_y: float, end_x: float, end_y: float):
92
+ """
93
+ Model çalıştırır, koordinatları parse eder, görsele yolu çizer.
94
+ """
95
+ if image is None:
96
+ return None, "❌ Lütfen bir harita görseli yükleyin."
97
+
98
+ # Prompt oluştur
99
+ prompt_text = (
100
+ f"You are provided an image of a path with a start location denoted in green "
101
+ f"and an end location denoted in red. \n"
102
+ f"The normalized xy-coordinates of the start location are ({start_x}, {start_y}) "
103
+ f"and of the end location ({end_x}, {end_y}). \n"
104
+ f"Output a list of normalized coordinates in the form of a list [(x1,y1), (x2,y2)...] "
105
+ f"of the path between the start and end location. \n"
106
+ f"Ensure that the path follows the traversable locations of the map."
107
+ )
108
+
109
+ messages = [
110
+ {
111
+ "role": "user",
112
+ "content": [
113
+ {"type": "image"},
114
+ {"type": "text", "text": prompt_text},
115
+ ],
116
+ }
117
+ ]
118
+
119
+ text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
120
+
121
+ inputs = processor(
122
+ text=[text],
123
+ images=[image],
124
+ return_tensors="pt",
125
+ padding=True,
126
+ min_pixels=256 * 28 * 28,
127
+ max_pixels=1024 * 768,
128
+ ).to(model.device)
129
+
130
+ with torch.no_grad():
131
+ generated_ids = model.generate(
132
+ **inputs,
133
+ max_new_tokens=512,
134
+ do_sample=False,
135
+ temperature=0.0,
136
+ )
137
+
138
+ raw_output = processor.decode(
139
+ generated_ids[0][inputs.input_ids.shape[1]:],
140
+ skip_special_tokens=True
141
+ ).strip()
142
+
143
+ # Koordinatları parse et
144
+ path = parse_coordinates(raw_output)
145
+
146
+ if not path:
147
+ return image, f"⚠️ Model geçerli koordinat üretemedi.\n\nHam çıktı:\n{raw_output}"
148
+
149
+ # Yolu görsele çiz
150
+ result_img = draw_path_on_image(image, path, (start_x, start_y), (end_x, end_y))
151
+
152
+ # Sonuç metni
153
+ path_str = " → ".join([f"({x:.4f}, {y:.4f})" for x, y in path])
154
+ info_text = (
155
+ f"✅ Tahmin edilen yol: **{len(path)} nokta**\n\n"
156
+ f"`{path_str}`\n\n"
157
+ f"---\n*Ham model çıktısı:*\n```\n{raw_output[:500]}\n```"
158
+ )
159
+
160
+ return result_img, info_text
161
+
162
+
163
+ # ── GRADIO ARAYÜZÜ ────────────────────────────────────────────────────────────
164
+
165
+ DESCRIPTION = """
166
+ # 🗺️ MapTrace Path Planner
167
+ **Model:** [Qwen3.5-0.8B-MapTrace-PartialFT](https://huggingface.co/TurkishCodeMan/Qwen3.5-0.8B-MapTrace-PartialFT)
168
+
169
+ Upload a map image and enter normalized start/end coordinates. The model will predict a traversable path between the two locations and overlay it on the map.
170
+
171
+ **Coordinates:** Normalized (x, y) values in [0, 1] range. Top-left = (0, 0), Bottom-right = (1, 1).
172
+ """
173
+
174
+ EXAMPLES = [
175
+ ["example_map.png", 0.77, 0.47, 0.54, 0.54],
176
+ ]
177
+
178
+ with gr.Blocks(
179
+ title="MapTrace Path Planner",
180
+ theme=gr.themes.Soft(primary_hue="emerald", secondary_hue="slate"),
181
+ css="""
182
+ .gradio-container { max-width: 1100px !important; }
183
+ .result-image img { border-radius: 12px; box-shadow: 0 4px 20px rgba(0,0,0,0.15); }
184
+ footer { display: none !important; }
185
+ """,
186
+ ) as demo:
187
+ gr.Markdown(DESCRIPTION)
188
+
189
+ with gr.Row():
190
+ # Sol panel: Giriş
191
+ with gr.Column(scale=1):
192
+ gr.Markdown("### 📤 Input")
193
+ input_image = gr.Image(
194
+ label="Map Image",
195
+ type="pil",
196
+ sources=["upload"],
197
+ height=350,
198
+ )
199
+ with gr.Row():
200
+ start_x = gr.Number(label="Start X", value=0.77, minimum=0.0, maximum=1.0, step=0.01, precision=4)
201
+ start_y = gr.Number(label="Start Y", value=0.47, minimum=0.0, maximum=1.0, step=0.01, precision=4)
202
+ with gr.Row():
203
+ end_x = gr.Number(label="End X", value=0.54, minimum=0.0, maximum=1.0, step=0.01, precision=4)
204
+ end_y = gr.Number(label="End Y", value=0.54, minimum=0.0, maximum=1.0, step=0.01, precision=4)
205
+
206
+ run_btn = gr.Button("🚀 Predict Path", variant="primary", size="lg")
207
+
208
+ # Right panel: Output
209
+ with gr.Column(scale=1):
210
+ gr.Markdown("### 📍 Result")
211
+ output_image = gr.Image(
212
+ label="Predicted Path",
213
+ type="pil",
214
+ interactive=False,
215
+ height=350,
216
+ elem_classes=["result-image"],
217
+ )
218
+ output_info = gr.Markdown(label="Info", value="*Results will appear here after running the model.*")
219
+
220
+ # Etiketler (Görsel referans)
221
+ with gr.Row():
222
+ gr.Markdown("""
223
+ ---
224
+ 🟢 **Start**   |   🔴 **End**   |   🟡 **Predicted Path**
225
+ """)
226
+
227
+ # Event
228
+ run_btn.click(
229
+ fn=run_inference,
230
+ inputs=[input_image, start_x, start_y, end_x, end_y],
231
+ outputs=[output_image, output_info],
232
+ api_name="predict_path",
233
+ show_progress="full",
234
+ )
235
+
236
+ # Enter ile de çalıştırılabilsin
237
+ for component in [start_x, start_y, end_x, end_y]:
238
+ component.submit(
239
+ fn=run_inference,
240
+ inputs=[input_image, start_x, start_y, end_x, end_y],
241
+ outputs=[output_image, output_info],
242
+ )
243
+
244
+ if __name__ == "__main__":
245
+ demo.launch(share=False)
requirements.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ transformers>=4.51.0
2
+ torch>=2.1.0
3
+ gradio>=5.0.0
4
+ Pillow>=10.0.0
5
+ accelerate>=0.26.0
6
+ huggingface_hub>=0.21.0
7
+ numpy>=1.24.0
upload_space.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ HuggingFace Space Upload Script
4
+ Bu script gradio_demo klasörünü HF Spaces'e yükler.
5
+ """
6
+
7
+ from huggingface_hub import HfApi, create_repo
8
+
9
+ # ── CONFIG ────────────────────────────────────────────────────────────────────
10
+ # 👇 Kendi HF kullanıcı adın ve space adını buraya yaz
11
+ HF_USERNAME = "TurkishCodeMan"
12
+ SPACE_NAME = "MapTrace-Path-Planner"
13
+ SPACE_DIR = "./" # Bu script gradio_demo klasöründen çalıştırılır
14
+
15
+ REPO_ID = f"{HF_USERNAME}/{SPACE_NAME}"
16
+
17
+
18
+ def main():
19
+ api = HfApi()
20
+
21
+ print("=" * 55)
22
+ print("🚀 HuggingFace Space Upload")
23
+ print("=" * 55)
24
+ print(f" Repo : {REPO_ID}")
25
+ print(f" SDK : gradio")
26
+ print("=" * 55)
27
+
28
+ # 1. Space oluştur (zaten varsa atla)
29
+ print("\n📦 Space oluşturuluyor / kontrol ediliyor...")
30
+ create_repo(
31
+ repo_id=REPO_ID,
32
+ repo_type="space",
33
+ space_sdk="gradio",
34
+ exist_ok=True,
35
+ private=False,
36
+ )
37
+ print(f" ✓ Space hazır: https://huggingface.co/spaces/{REPO_ID}")
38
+
39
+ # 2. Klasörü yükle
40
+ print(f"\n📤 Dosyalar yükleniyor: {SPACE_DIR} → {REPO_ID}")
41
+ api.upload_folder(
42
+ folder_path=SPACE_DIR,
43
+ repo_id=REPO_ID,
44
+ repo_type="space",
45
+ commit_message="Update MapTrace Gradio demo",
46
+ # upload_space.py'nin kendisini IGNORE listesine ekle (gereksiz)
47
+ ignore_patterns=["*.pyc", "__pycache__", ".git"],
48
+ )
49
+
50
+ print("\n✅ YÜKLEME TAMAMLANDI!")
51
+ print(f" Space URL : https://huggingface.co/spaces/{REPO_ID}")
52
+ print(f" Birkaç dakika içinde Space build edilecektir.")
53
+
54
+
55
+ if __name__ == "__main__":
56
+ main()