flashstudio 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of flashstudio might be problematic. Click here for more details.

Files changed (97) hide show
  1. flashstudio/__init__.py +5 -0
  2. flashstudio/app.py +194 -0
  3. flashstudio/cli.py +18 -0
  4. flashstudio/components/__init__.py +0 -0
  5. flashstudio/components/project_manager.py +490 -0
  6. flashstudio/components/sidebar.py +87 -0
  7. flashstudio/components/styles.py +346 -0
  8. flashstudio/components/wizard.py +45 -0
  9. flashstudio/components/zone_drawer/__init__.py +177 -0
  10. flashstudio/constants.py +245 -0
  11. flashstudio/core/__init__.py +3 -0
  12. flashstudio/core/constants.py +3 -0
  13. flashstudio/launcher.py +288 -0
  14. flashstudio/pages/__init__.py +8 -0
  15. flashstudio/pages/dashboard/__init__.py +3 -0
  16. flashstudio/pages/dashboard/_common.py +10 -0
  17. flashstudio/pages/dashboard/overview/__init__.py +3 -0
  18. flashstudio/pages/dashboard/overview/metrics.py +33 -0
  19. flashstudio/pages/dashboard/overview/pipeline_status.py +35 -0
  20. flashstudio/pages/dashboard/overview/project_banner.py +23 -0
  21. flashstudio/pages/dashboard/overview/quick_actions.py +28 -0
  22. flashstudio/pages/dashboard/overview/recent_runs.py +99 -0
  23. flashstudio/pages/dashboard/overview/tab.py +35 -0
  24. flashstudio/pages/dashboard/page.py +17 -0
  25. flashstudio/pages/data/__init__.py +3 -0
  26. flashstudio/pages/data/_common.py +107 -0
  27. flashstudio/pages/data/download/__init__.py +3 -0
  28. flashstudio/pages/data/download/tab.py +75 -0
  29. flashstudio/pages/data/helpers.py +338 -0
  30. flashstudio/pages/data/page.py +50 -0
  31. flashstudio/pages/data/preview/__init__.py +3 -0
  32. flashstudio/pages/data/preview/tab.py +139 -0
  33. flashstudio/pages/data/upload/__init__.py +3 -0
  34. flashstudio/pages/data/upload/tab.py +156 -0
  35. flashstudio/pages/data/verify/__init__.py +3 -0
  36. flashstudio/pages/data/verify/tab.py +108 -0
  37. flashstudio/pages/export/__init__.py +3 -0
  38. flashstudio/pages/export/page.py +54 -0
  39. flashstudio/pages/export/weights/__init__.py +4 -0
  40. flashstudio/pages/export/weights/exporter.py +60 -0
  41. flashstudio/pages/export/weights/settings.py +86 -0
  42. flashstudio/pages/inference/__init__.py +3 -0
  43. flashstudio/pages/inference/_common.py +76 -0
  44. flashstudio/pages/inference/data/__init__.py +3 -0
  45. flashstudio/pages/inference/data/tab.py +121 -0
  46. flashstudio/pages/inference/model/__init__.py +3 -0
  47. flashstudio/pages/inference/model/tab.py +62 -0
  48. flashstudio/pages/inference/page.py +42 -0
  49. flashstudio/pages/inference/run/__init__.py +3 -0
  50. flashstudio/pages/inference/run/detection.py +133 -0
  51. flashstudio/pages/inference/run/tab.py +161 -0
  52. flashstudio/pages/inference/run/video.py +195 -0
  53. flashstudio/pages/inference/solution/__init__.py +3 -0
  54. flashstudio/pages/inference/solution/tab.py +121 -0
  55. flashstudio/pages/model/__init__.py +3 -0
  56. flashstudio/pages/model/_common.py +3 -0
  57. flashstudio/pages/model/advanced/__init__.py +3 -0
  58. flashstudio/pages/model/advanced/tab.py +54 -0
  59. flashstudio/pages/model/architecture/__init__.py +3 -0
  60. flashstudio/pages/model/architecture/tab.py +90 -0
  61. flashstudio/pages/model/augment/__init__.py +3 -0
  62. flashstudio/pages/model/augment/tab.py +18 -0
  63. flashstudio/pages/model/hyperparams/__init__.py +4 -0
  64. flashstudio/pages/model/hyperparams/tab.py +89 -0
  65. flashstudio/pages/model/page.py +30 -0
  66. flashstudio/pages/model/utils/__init__.py +3 -0
  67. flashstudio/pages/model/utils/config.py +27 -0
  68. flashstudio/pages/training/__init__.py +3 -0
  69. flashstudio/pages/training/_common.py +24 -0
  70. flashstudio/pages/training/launch/__init__.py +3 -0
  71. flashstudio/pages/training/launch/controls.py +84 -0
  72. flashstudio/pages/training/launch/dialogs.py +247 -0
  73. flashstudio/pages/training/launch/preflight.py +77 -0
  74. flashstudio/pages/training/launch/runner.py +440 -0
  75. flashstudio/pages/training/launch/tab.py +134 -0
  76. flashstudio/pages/training/monitor/__init__.py +3 -0
  77. flashstudio/pages/training/monitor/checkpoints.py +96 -0
  78. flashstudio/pages/training/monitor/curves.py +113 -0
  79. flashstudio/pages/training/monitor/dashboard.py +129 -0
  80. flashstudio/pages/training/monitor/gt_verification.py +122 -0
  81. flashstudio/pages/training/monitor/log_viewer.py +43 -0
  82. flashstudio/pages/training/monitor/parsers.py +253 -0
  83. flashstudio/pages/training/monitor/run_meta.py +165 -0
  84. flashstudio/pages/training/monitor/tab.py +142 -0
  85. flashstudio/pages/training/monitor/visualizations.py +52 -0
  86. flashstudio/pages/training/page.py +134 -0
  87. flashstudio/utils/__init__.py +152 -0
  88. flashstudio/utils/config.py +104 -0
  89. flashstudio/utils/device.py +67 -0
  90. flashstudio/utils/filesystem.py +67 -0
  91. flashstudio/utils/jobs.py +95 -0
  92. flashstudio/utils/training_hooks.py +266 -0
  93. flashstudio-0.0.1.dist-info/METADATA +107 -0
  94. flashstudio-0.0.1.dist-info/RECORD +97 -0
  95. flashstudio-0.0.1.dist-info/WHEEL +5 -0
  96. flashstudio-0.0.1.dist-info/entry_points.txt +2 -0
  97. flashstudio-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,195 @@
1
+ """Inference — Video processing utilities."""
2
+
3
+ import os
4
+ import time
5
+ import tempfile
6
+ import streamlit as st
7
+ from PIL import Image, ImageDraw
8
+ from flashstudio.constants import (
9
+ COCO_CLASSES, INFER_CONF_THRESHOLD, INFER_NMS_THRESHOLD,
10
+ INFER_IMG_SIZE, INFER_MAX_FRAMES, INFER_FRAME_SKIP,
11
+ INFER_DEFAULT_RESOLUTION, COLOR_PRIMARY,
12
+ )
13
+ from flashstudio.pages.inference._common import (
14
+ _get_class_names, _HAS_PREDICTOR, _SOLUTIONS_AVAILABLE,
15
+ )
16
+ from flashstudio.pages.inference.run.detection import _draw_boxes_cv2
17
+
18
+ try:
19
+ from flashdet import Predictor, FlashTracker
20
+ except ImportError:
21
+ pass
22
+
23
+
24
+ def _run_video():
25
+ video = st.session_state.get("infer_video")
26
+ solution = st.session_state.get("selected_solution", "None (Detection Only)")
27
+ weights = st.session_state.get("infer_weights_path", "") or st.session_state.get("infer_weights_file")
28
+ if weights:
29
+ try:
30
+ _run_flashdet_video(video, solution)
31
+ return
32
+ except Exception as e:
33
+ st.warning(f"FlashDet error: {e}. Demo mode.")
34
+ _run_demo_video(solution)
35
+
36
+
37
+ def _run_demo_video(solution_name):
38
+ t0 = time.perf_counter()
39
+ progress = st.progress(0)
40
+ frames = []
41
+ for i in range(4):
42
+ time.sleep(0.2)
43
+ progress.progress((i + 1) / 4)
44
+ img = Image.new("RGB", INFER_DEFAULT_RESOLUTION, (248, 248, 252))
45
+ draw = ImageDraw.Draw(img)
46
+ draw.rectangle([80 + i * 40, 80 + i * 30, 250 + i * 40, 230 + i * 30], outline=COLOR_PRIMARY, width=3)
47
+ draw.text((90 + i * 40, 65 + i * 30), f"car 0.{85 + i}", fill=COLOR_PRIMARY)
48
+ frames.append(img)
49
+ progress.empty()
50
+ elapsed = time.perf_counter() - t0
51
+
52
+ sol_output = {}
53
+ if "Counter" in solution_name:
54
+ sol_output = {"in_count": 12, "out_count": 8}
55
+ elif "Region" in solution_name:
56
+ sol_output = {"region_counts": {"Zone A": 5, "Zone B": 3}}
57
+ elif "Speed" in solution_name:
58
+ sol_output = {"speeds": [[1, 45.2], [2, 38.7], [3, 52.1]]}
59
+
60
+ st.session_state["video_results"] = {
61
+ "frames": 300, "detections": 1247, "processing_fps": 300 / max(elapsed, 0.001),
62
+ "elapsed_time": elapsed, "solution_output": sol_output, "frames_preview": frames,
63
+ "output_path": _get_output_path("output.mp4") if st.session_state.get("infer_save_video") else None,
64
+ }
65
+ st.rerun()
66
+
67
+
68
+ def _get_output_path(filename):
69
+ """Resolve output path: prefer save_dir, fall back to tempdir."""
70
+ sd = st.session_state.get("save_dir", "")
71
+ if sd and os.path.isdir(sd):
72
+ return os.path.join(sd, filename)
73
+ return os.path.join(tempfile.gettempdir(), filename)
74
+
75
+
76
+ def _run_flashdet_video(video_file, solution_name):
77
+ if not _HAS_PREDICTOR:
78
+ raise ImportError("Predictor unavailable")
79
+ import cv2
80
+ tmp = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False)
81
+ tmp.write(video_file.read()); tmp.flush(); video_file.seek(0)
82
+ cap = cv2.VideoCapture(tmp.name)
83
+ total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
84
+ fps = cap.get(cv2.CAP_PROP_FPS) or 30
85
+
86
+ weights = st.session_state.get("infer_weights_path", "")
87
+ if not weights:
88
+ up = st.session_state.get("infer_weights_file")
89
+ if up:
90
+ wtmp = tempfile.NamedTemporaryFile(suffix=".pth", delete=False)
91
+ wtmp.write(up.read()); wtmp.flush(); up.seek(0)
92
+ weights = wtmp.name
93
+
94
+ custom_cls = _get_class_names()
95
+ predictor = Predictor(model_path=weights, device=st.session_state.get("infer_device", "cpu").split(" ")[0],
96
+ conf_thresh=st.session_state.get("infer_conf", INFER_CONF_THRESHOLD),
97
+ nms_thresh=st.session_state.get("infer_nms", INFER_NMS_THRESHOLD),
98
+ input_size=st.session_state.get("infer_img_size", INFER_IMG_SIZE),
99
+ class_names=custom_cls if custom_cls != COCO_CLASSES else None)
100
+ tracker = FlashTracker()
101
+ solution = _create_solution(solution_name, predictor, tracker, fps)
102
+
103
+ max_frames = st.session_state.get("infer_max_frames", INFER_MAX_FRAMES) or total
104
+ skip = st.session_state.get("infer_frame_skip", INFER_FRAME_SKIP)
105
+ frames_preview, processed, total_dets = [], 0, 0
106
+ t0 = time.perf_counter()
107
+ progress = st.progress(0)
108
+
109
+ for idx in range(min(total, max_frames)):
110
+ ret, frame = cap.read()
111
+ if not ret:
112
+ break
113
+ if idx % skip != 0:
114
+ continue
115
+ if solution:
116
+ annotated, results = solution.process_frame(frame)
117
+ else:
118
+ results = predictor(frame)
119
+ annotated = _draw_boxes_cv2(frame, results)
120
+ if isinstance(results, (list, tuple)):
121
+ total_dets += len(results)
122
+ processed += 1
123
+ if idx % max(total // 4, 1) == 0:
124
+ frames_preview.append(Image.fromarray(cv2.cvtColor(annotated or frame, cv2.COLOR_BGR2RGB)))
125
+ progress.progress(idx / min(total, max_frames))
126
+
127
+ cap.release(); progress.empty()
128
+ try:
129
+ os.unlink(tmp.name)
130
+ except OSError:
131
+ pass
132
+ elapsed = time.perf_counter() - t0
133
+ sol_output = {}
134
+ if solution:
135
+ try:
136
+ sol_output = solution.get_results()
137
+ if not isinstance(sol_output, dict):
138
+ sol_output = {}
139
+ except Exception:
140
+ pass
141
+
142
+ st.session_state["video_results"] = {
143
+ "frames": processed, "detections": total_dets, "processing_fps": processed / max(elapsed, 0.001),
144
+ "elapsed_time": elapsed, "solution_output": sol_output, "frames_preview": frames_preview,
145
+ "output_path": _get_output_path("output.mp4") if st.session_state.get("infer_save_video") else None,
146
+ }
147
+ st.rerun()
148
+
149
+
150
+ def _create_solution(solution_name, predictor, tracker, fps):
151
+ line_pts = st.session_state.get("zone_line_points")
152
+ polygons = st.session_state.get("zone_polygons")
153
+ class_filter_names = st.session_state.get("infer_class_filter") or None
154
+ classes = None
155
+ if class_filter_names:
156
+ all_cls = _get_class_names()
157
+ classes = [all_cls.index(n) for n in class_filter_names if n in all_cls]
158
+ if not classes:
159
+ classes = None
160
+
161
+ if solution_name == "None (Detection Only)":
162
+ return None
163
+
164
+ first_poly = polygons[0] if polygons and len(polygons) > 0 else []
165
+ sol_map = {
166
+ "Object Counter": ("ObjectCounter", dict(line_points=line_pts, classes=classes)),
167
+ "Region Counter": ("RegionCounter", dict(regions={"zone_0": first_poly} if first_poly else {}, classes=classes)),
168
+ "Speed Estimator": ("SpeedEstimator", dict(fps=fps, classes=classes)),
169
+ "Heatmap": ("Heatmap", dict(classes=classes)),
170
+ "Security Alarm": ("SecurityAlarm", dict(restricted_zones=polygons or [], classes=classes)),
171
+ "Trajectory": ("TrajectoryVisualizer", dict(classes=classes)),
172
+ "Blurrer": ("ObjectBlurrer", dict(classes=classes)),
173
+ "Queue": ("QueueManager", dict(queue_regions=polygons or [], classes=classes, fps=fps)),
174
+ "Crowd": ("CrowdDensity", dict(classes=classes)),
175
+ "Parking": ("ParkingManager", dict(parking_spots=polygons or [], classes=classes)),
176
+ "Traffic": ("TrafficFlow", dict(fps=fps, classes=classes)),
177
+ "Dwell": ("DwellTimeAnalyzer", dict(zones={"zone_0": first_poly} if first_poly else {}, fps=fps, classes=classes)),
178
+ "Distance": ("DistanceCalculator", dict(classes=classes)),
179
+ "Workout": ("WorkoutMonitor", dict(classes=classes)),
180
+ "Cropper": ("ObjectCropper", dict(classes=classes)),
181
+ "Analytics": ("AnalyticsDashboard", dict(classes=classes)),
182
+ }
183
+
184
+ for keyword, (cls_name, kwargs) in sol_map.items():
185
+ if keyword in solution_name:
186
+ cls = _SOLUTIONS_AVAILABLE.get(cls_name)
187
+ if not cls:
188
+ return None
189
+ try:
190
+ if cls_name not in ("Heatmap", "ObjectBlurrer", "CrowdDensity"):
191
+ return cls(predictor=predictor, tracker=tracker, **kwargs)
192
+ return cls(predictor=predictor, **kwargs)
193
+ except TypeError:
194
+ return cls(predictor=predictor, **kwargs)
195
+ return None
@@ -0,0 +1,3 @@
1
+ """Inference — Solution tab sub-package."""
2
+
3
+ from flashstudio.pages.inference.solution.tab import _tab_solution # noqa: F401
@@ -0,0 +1,121 @@
1
+ """Inference — Solution tab."""
2
+
3
+ import streamlit as st
4
+ from flashstudio.constants import (
5
+ INFER_DISPLAY_WIDTH, INFER_DEFAULT_RESOLUTION,
6
+ ZONE_MAX_X, ZONE_MAX_Y, ZONE_DEFAULT_LINE, ZONE_DEFAULT_POLYGON,
7
+ )
8
+ from flashstudio.pages.inference._common import (
9
+ SOLUTIONS, _SOLUTIONS_AVAILABLE, _solution_classes, _HAS_ZONE_DRAWER,
10
+ )
11
+ from flashstudio.pages.inference.data.tab import _get_first_frame
12
+
13
+ if _HAS_ZONE_DRAWER:
14
+ from flashstudio.components.zone_drawer import zone_drawer
15
+
16
+
17
+ def _tab_solution():
18
+ sc1, sc2 = st.columns([2, 3])
19
+ with sc1:
20
+ selected = st.selectbox("Solution", list(SOLUTIONS.keys()), key="selected_solution",
21
+ format_func=lambda x: x)
22
+ sol = SOLUTIONS[selected]
23
+ st.caption(sol["desc"])
24
+ avail = len(_SOLUTIONS_AVAILABLE)
25
+ total = len(_solution_classes)
26
+ st.caption(f"{avail}/{total} solutions available")
27
+
28
+ with sc2:
29
+ if sol["needs_zone"]:
30
+ st.info("Zone required — draw below using Polygon, Rectangle, or Line.")
31
+ else:
32
+ st.caption("Zone optional — draw one to limit detection area.")
33
+ has_line = bool(st.session_state.get("zone_line_points"))
34
+ has_poly = bool(st.session_state.get("zone_polygons"))
35
+ if has_line or has_poly:
36
+ zone_type = "Line" if has_line else "Polygon"
37
+ st.success(f"Zone set: {zone_type}")
38
+ elif sol["needs_zone"]:
39
+ st.warning("No zone defined yet")
40
+
41
+ _zone_draw_ui(sol)
42
+
43
+
44
+ def _zone_draw_ui(sol):
45
+ default_type = sol.get("zone_type", "polygon")
46
+ mode_options = ["polygon", "rect", "line"]
47
+ default_idx = mode_options.index(default_type) if default_type in mode_options else 0
48
+ draw_mode = st.radio("Draw Mode", mode_options, index=default_idx, horizontal=True, key="zone_draw_mode",
49
+ format_func=lambda x: {"polygon": "Polygon", "rect": "Rectangle", "line": "Line"}[x])
50
+
51
+ frame_img = _get_first_frame()
52
+
53
+ existing_points = st.session_state.get("zone_draw_points", [])
54
+ existing_closed = st.session_state.get("zone_closed", False)
55
+
56
+ if _HAS_ZONE_DRAWER:
57
+ result = zone_drawer(image=frame_img, mode=draw_mode, points=existing_points,
58
+ closed=existing_closed, display_width=INFER_DISPLAY_WIDTH)
59
+ if result and result.get("points"):
60
+ pts = result["points"]
61
+ img_w = frame_img.size[0] if frame_img else INFER_DEFAULT_RESOLUTION[0]
62
+ img_h = frame_img.size[1] if frame_img else INFER_DEFAULT_RESOLUTION[1]
63
+ dw = result.get("displayWidth", INFER_DISPLAY_WIDTH)
64
+ dh = result.get("displayHeight", INFER_DEFAULT_RESOLUTION[1])
65
+ sx = img_w / dw if dw else 1
66
+ sy = img_h / dh if dh else 1
67
+ _store_zone_coords(pts, draw_mode, sx, sy)
68
+ else:
69
+ st.warning("Zone drawing component not available.")
70
+
71
+ with st.expander("Set coordinates manually", expanded=not _HAS_ZONE_DRAWER):
72
+ _manual_zone_input(draw_mode)
73
+
74
+
75
+ def _manual_zone_input(draw_mode):
76
+ if draw_mode == "line":
77
+ c1, c2 = st.columns(2)
78
+ with c1:
79
+ x1 = st.number_input("X1", 0, ZONE_MAX_X, ZONE_DEFAULT_LINE[0][0], key="manual_line_x1")
80
+ y1 = st.number_input("Y1", 0, ZONE_MAX_Y, ZONE_DEFAULT_LINE[0][1], key="manual_line_y1")
81
+ with c2:
82
+ x2 = st.number_input("X2", 0, ZONE_MAX_X, ZONE_DEFAULT_LINE[1][0], key="manual_line_x2")
83
+ y2 = st.number_input("Y2", 0, ZONE_MAX_Y, ZONE_DEFAULT_LINE[1][1], key="manual_line_y2")
84
+ if st.button("Set Line", key="set_manual_line"):
85
+ st.session_state["zone_line_points"] = [(x1, y1), (x2, y2)]
86
+ st.success(f"({x1},{y1}) → ({x2},{y2})")
87
+ else:
88
+ pts = st.text_area("Points (x,y per line)", ZONE_DEFAULT_POLYGON, key="manual_polygon_text")
89
+ btn_label = "Set Rectangle" if draw_mode == "rect" else "Set Polygon"
90
+ min_pts = 2 if draw_mode == "rect" else 3
91
+ if st.button(btn_label, key="set_manual_polygon"):
92
+ parsed = []
93
+ for line in pts.strip().split("\n"):
94
+ p = line.strip().split(",")
95
+ if len(p) == 2:
96
+ try:
97
+ parsed.append((int(p[0].strip()), int(p[1].strip())))
98
+ except ValueError:
99
+ continue
100
+ if len(parsed) >= min_pts:
101
+ if draw_mode == "rect" and len(parsed) >= 2:
102
+ x1, y1 = parsed[0]; x2, y2 = parsed[1]
103
+ st.session_state["zone_polygons"] = [[(x1, y1), (x2, y1), (x2, y2), (x1, y2)]]
104
+ st.success(f"Rectangle: ({x1},{y1}) → ({x2},{y2})")
105
+ else:
106
+ st.session_state["zone_polygons"] = [parsed]
107
+ st.success(f"{len(parsed)} vertices")
108
+
109
+
110
+ def _store_zone_coords(display_points, draw_mode, scale_x, scale_y):
111
+ orig = [(int(x * scale_x), int(y * scale_y)) for x, y in display_points]
112
+ if draw_mode == "line" and len(orig) >= 2:
113
+ st.session_state["zone_line_points"] = [orig[0], orig[1]]
114
+ st.success(f"Line: {orig[0]} → {orig[1]}")
115
+ elif draw_mode == "rect" and len(orig) >= 2:
116
+ x1, y1 = orig[0]; x2, y2 = orig[1]
117
+ st.session_state["zone_polygons"] = [[(x1, y1), (x2, y1), (x2, y2), (x1, y2)]]
118
+ st.success(f"Rect: ({x1},{y1})→({x2},{y2})")
119
+ elif draw_mode == "polygon" and len(orig) >= 3:
120
+ st.session_state["zone_polygons"] = [orig]
121
+ st.success(f"Polygon: {len(orig)} pts") if st.session_state.get("zone_closed") else st.info(f"{len(orig)} pts, click more or close")
@@ -0,0 +1,3 @@
1
+ """FlashStudio — Model page package."""
2
+
3
+ from flashstudio.pages.model.page import render_model_page # noqa: F401
@@ -0,0 +1,3 @@
1
+ """Shared constants and imports for model page tabs."""
2
+
3
+ from flashstudio.constants import ARCH_FAMILIES, DEFAULT_MODEL_ARCH # noqa: F401
@@ -0,0 +1,3 @@
1
+ """Model advanced options sub-package."""
2
+
3
+ from flashstudio.pages.model.advanced.tab import _tab_adv # noqa: F401
@@ -0,0 +1,54 @@
1
+ """FlashStudio — Advanced options tab."""
2
+
3
+ import streamlit as st
4
+
5
+
6
+ def _tab_adv():
7
+ col1, col2 = st.columns(2)
8
+ with col1:
9
+ with st.container(border=True):
10
+ st.markdown("#### Memory")
11
+ ac1, ac2 = st.columns(2)
12
+ with ac1:
13
+ st.checkbox("Act. Checkpoint", False, key="activation_checkpointing")
14
+ st.checkbox("Act. Offload", False, key="activation_offloading")
15
+ with ac2:
16
+ st.checkbox("Optim in Bwd", False, key="optimizer_in_bwd")
17
+ st.checkbox("torch.compile", False, key="compile_model")
18
+ st.checkbox("8-bit Optimizer", False, key="use_8bit_optimizer")
19
+ st.checkbox("Chunked Loss", False, key="chunked_loss")
20
+ if st.session_state.get("chunked_loss"):
21
+ st.number_input("Chunk Size", 256, 4096, 1024, key="chunk_size")
22
+
23
+ with col2:
24
+ with st.container(border=True):
25
+ st.markdown("#### Distributed & IO")
26
+ st.checkbox("Multi-GPU (DDP)", False, key="ddp")
27
+
28
+ nc = st.session_state.get("num_classes", 0)
29
+ cls_names = st.session_state.get("class_names", "")
30
+ if isinstance(cls_names, list):
31
+ cls_names = "\n".join(cls_names)
32
+ if nc and cls_names.strip():
33
+ names_list = [c.strip() for c in cls_names.strip().split("\n") if c.strip()]
34
+ preview = ", ".join(names_list[:5])
35
+ if len(names_list) > 5:
36
+ preview += f" ... (+{len(names_list) - 5})"
37
+ st.caption(f"Classes ({nc}): {preview}")
38
+ else:
39
+ st.caption("Classes: not set — go to Data → Upload")
40
+
41
+ st.text_input("Class File (.txt)", placeholder="auto-generated from dataset classes",
42
+ key="class_file", help="Leave empty to auto-generate from dataset classes")
43
+
44
+ from flashstudio.utils import DEFAULTS
45
+ if "save_dir" not in st.session_state or not st.session_state.get("save_dir", "").strip():
46
+ st.session_state["save_dir"] = DEFAULTS["save_dir"]
47
+ st.text_input("Save Dir", key="save_dir", placeholder=DEFAULTS["save_dir"])
48
+ rc1, rc2 = st.columns(2)
49
+ with rc1:
50
+ st.checkbox("Save Best Only", True, key="save_best")
51
+ with rc2:
52
+ st.checkbox("Resume", False, key="resume_training")
53
+ if st.session_state.get("resume_training"):
54
+ st.text_input("Checkpoint", placeholder="checkpoint_last.pth", key="resume_path")
@@ -0,0 +1,3 @@
1
+ """Model architecture sub-package."""
2
+
3
+ from flashstudio.pages.model.architecture.tab import _tab_arch, _pretrain_finetune # noqa: F401
@@ -0,0 +1,90 @@
1
+ """FlashStudio — Model architecture tab."""
2
+
3
+ import streamlit as st
4
+ from flashstudio.constants import (
5
+ FLASHDET_MODELS, LORA_VARIANTS, ARCH_FAMILIES,
6
+ )
7
+
8
+
9
+ def _tab_arch():
10
+ arch = st.radio("Family", ARCH_FAMILIES, key="arch_family", horizontal=True)
11
+
12
+ # Head config — num_classes from dataset
13
+ nc = st.session_state.get("num_classes", 0)
14
+ cls_names = st.session_state.get("class_names", "")
15
+ if isinstance(cls_names, list):
16
+ cls_names = "\n".join(cls_names)
17
+ if not nc and cls_names.strip():
18
+ nc = len([c for c in cls_names.strip().split("\n") if c.strip()])
19
+ st.session_state["num_classes"] = nc
20
+
21
+ if arch == "FlashDet (recommended)":
22
+ col_sel, col_info = st.columns([2, 3])
23
+ with col_sel:
24
+ model = st.radio("Size", list(FLASHDET_MODELS.keys()), index=0, key="model_arch")
25
+ info = FLASHDET_MODELS[model]
26
+ if model == "FlashDet-Pico":
27
+ st.selectbox("Backbone", ["LiteBackbone", "PicoBackbone"], key="pico_backbone")
28
+ else:
29
+ st.caption(f"Backbone: {info['backbone']}")
30
+ st.caption(f"{info['neck']} → {info['head']} · CIoU+BCE+L1 · STAL · ProgLoss")
31
+
32
+ with col_info:
33
+ m1, m2, m3, m4 = st.columns(4)
34
+ with m1:
35
+ st.metric("Params", info["params"])
36
+ with m2:
37
+ st.metric("Speed", info["speed"])
38
+ with m3:
39
+ st.metric("Best For", info["for"])
40
+ with m4:
41
+ st.metric("Head Classes", nc if nc else "—")
42
+ if nc:
43
+ st.caption(f"Model head output → **{nc}** classes (auto-set from dataset)")
44
+ else:
45
+ st.info("No classes detected yet. Go to **Data** → **Upload** to load a dataset and classes will be set automatically.")
46
+ _pretrain_finetune()
47
+ else:
48
+ name = arch.replace(" (recommended)", "")
49
+ col_sel, col_info = st.columns([2, 3])
50
+ with col_sel:
51
+ st.slider("Width ×", 0.25, 1.5, 0.5, 0.25, key="yolo_width_mult")
52
+ st.slider("Depth ×", 0.33, 1.5, 0.33, 0.33, key="yolo_depth_mult")
53
+ if name == "YOLOv9":
54
+ st.checkbox("PGI", True, key="yolo_use_pgi")
55
+ elif name == "YOLOv10":
56
+ st.checkbox("PSA", True, key="yolo_use_psa")
57
+ elif name == "YOLOv11":
58
+ st.checkbox("C2PSA", True, key="yolo_use_c2psa")
59
+ with col_info:
60
+ st.caption(f"{name} — fixed backbone/neck/head. Control via width/depth multipliers.")
61
+ if nc:
62
+ st.metric("Head Classes", nc)
63
+ _pretrain_finetune()
64
+
65
+
66
+ def _pretrain_finetune():
67
+ with st.container(border=True):
68
+ c1, c2 = st.columns(2)
69
+ with c1:
70
+ st.markdown("#### Pretrained")
71
+ pt = st.radio("Init", ["COCO pretrained", "ImageNet backbone", "Random", "Custom"],
72
+ key="pretrain_option", label_visibility="collapsed")
73
+ if pt == "Custom":
74
+ st.text_input("Path", placeholder="/path/model.pth", key="custom_weights")
75
+ with c2:
76
+ st.markdown("#### Fine-tune")
77
+ ft = st.radio("Mode", ["Full fine-tune", "Freeze backbone", "Freeze BB+neck", "LoRA"],
78
+ key="finetune_strategy", label_visibility="collapsed")
79
+ if ft == "LoRA":
80
+ lc1, lc2 = st.columns(2)
81
+ with lc1:
82
+ st.selectbox("Variant", LORA_VARIANTS, key="lora_variant")
83
+ st.slider("Rank", 4, 64, 16, key="lora_rank")
84
+ with lc2:
85
+ st.slider("Alpha", 8, 128, 32, key="lora_alpha")
86
+ st.slider("Dropout", 0.0, 0.5, 0.0, key="lora_dropout")
87
+ st.multiselect("Targets", ["backbone", "neck", "head"], default=["backbone", "neck"], key="lora_targets")
88
+ st.checkbox("QLoRA", False, key="qlora")
89
+ if st.session_state.get("qlora"):
90
+ st.selectbox("Quant", ["int8", "nf4"], key="qlora_dtype")
@@ -0,0 +1,3 @@
1
+ """Model augmentation sub-package."""
2
+
3
+ from flashstudio.pages.model.augment.tab import _tab_aug # noqa: F401
@@ -0,0 +1,18 @@
1
+ """FlashStudio — Augmentation tab."""
2
+
3
+ import streamlit as st
4
+
5
+
6
+ def _tab_aug():
7
+ col1, col2 = st.columns(2)
8
+ with col1:
9
+ with st.container(border=True):
10
+ st.markdown("#### Multi-Image")
11
+ st.checkbox("Mosaic (4-image)", True, key="aug_mosaic")
12
+ st.checkbox("MixUp", False, key="aug_mixup")
13
+ st.checkbox("CopyPaste", False, key="aug_copypaste")
14
+ with col2:
15
+ with st.container(border=True):
16
+ st.markdown("#### Built-in (always on)")
17
+ for a in ["Scale jitter 0.5–1.5×", "Horizontal flip", "Color jitter", "Letterbox resize", "ImageNet norm"]:
18
+ st.markdown(f'<span style="font-size:0.88rem;color:#4B5563;">• {a}</span>', unsafe_allow_html=True)
@@ -0,0 +1,4 @@
1
+ """Model hyperparams sub-package."""
2
+
3
+ from flashstudio.pages.model.hyperparams.tab import _tab_hyper, _summary_bar # noqa: F401
4
+ from flashstudio.pages.model.utils.config import _build_cfg, _apply_cfg # noqa: F401
@@ -0,0 +1,89 @@
1
+ """FlashStudio — Hyperparameters tab, summary bar."""
2
+
3
+ import yaml
4
+ import streamlit as st
5
+ from flashstudio.constants import (
6
+ OPTIMIZERS,
7
+ TRAIN_EPOCHS, TRAIN_BATCH_SIZE, TRAIN_LR, TRAIN_IMG_SIZE,
8
+ TRAIN_WEIGHT_DECAY, TRAIN_WARMUP_EPOCHS, TRAIN_PATIENCE,
9
+ TRAIN_NUM_WORKERS, TRAIN_GRAD_ACCUM, TRAIN_VAL_INTERVAL,
10
+ TRAIN_LR_FINAL_RATIO, BATCH_SIZE_OPTIONS, IMG_SIZE_OPTIONS,
11
+ DEFAULT_MODEL_ARCH,
12
+ )
13
+
14
+
15
+ def _tab_hyper():
16
+ col1, col2 = st.columns(2)
17
+ with col1:
18
+ with st.container(border=True):
19
+ st.markdown("#### Core")
20
+ hc1, hc2 = st.columns(2)
21
+ with hc1:
22
+ st.number_input("Epochs", min_value=1, max_value=10000, value=TRAIN_EPOCHS, step=10, key="epochs")
23
+ st.select_slider("Batch", BATCH_SIZE_OPTIONS, value=TRAIN_BATCH_SIZE, key="batch_size")
24
+ st.select_slider("Img Size", IMG_SIZE_OPTIONS, value=TRAIN_IMG_SIZE, key="img_size")
25
+ with hc2:
26
+ st.slider("LR", 1e-5, 1e-1, TRAIN_LR, format="%.5f", key="lr")
27
+ st.slider("Weight Decay", 0.0, 0.1, TRAIN_WEIGHT_DECAY, format="%.3f", key="weight_decay")
28
+ st.selectbox("Optimizer", OPTIMIZERS, key="optimizer")
29
+
30
+ with col2:
31
+ with st.container(border=True):
32
+ st.markdown("#### Schedule & Options")
33
+ sc1, sc2 = st.columns(2)
34
+ with sc1:
35
+ st.slider("Warmup Ep", 0, 20, TRAIN_WARMUP_EPOCHS, key="warmup_epochs")
36
+ st.slider("LR Final ×", 0.01, 0.5, TRAIN_LR_FINAL_RATIO, format="%.2f", key="lr_final_ratio")
37
+ st.slider("Workers", 0, 16, TRAIN_NUM_WORKERS, key="num_workers")
38
+ with sc2:
39
+ st.slider("Patience", 5, 100, TRAIN_PATIENCE, key="patience")
40
+ st.number_input("Grad Accum", 1, 16, TRAIN_GRAD_ACCUM, key="grad_accum")
41
+ st.slider("Val Interval", 1, 20, TRAIN_VAL_INTERVAL, key="val_interval")
42
+ oc1, oc2, oc3 = st.columns(3)
43
+ with oc1:
44
+ st.checkbox("AMP FP16", True, key="amp")
45
+ with oc2:
46
+ st.checkbox("Grad Clip", True, key="grad_clip")
47
+ with oc3:
48
+ st.checkbox("Multi-Scale", False, key="multiscale")
49
+
50
+
51
+ def _summary_bar():
52
+ model = st.session_state.get("model_arch", DEFAULT_MODEL_ARCH)
53
+ nc = st.session_state.get("num_classes", 0)
54
+ cls_names = st.session_state.get("class_names", "")
55
+ if isinstance(cls_names, list):
56
+ cls_names = "\n".join(cls_names)
57
+ nc_display = nc if nc else len([c for c in cls_names.strip().split("\n") if c.strip()]) if cls_names.strip() else "—"
58
+ st.markdown(
59
+ f'<div class="info-bar">'
60
+ f'Model: <b>{model.replace("FlashDet-", "")}</b> · '
61
+ f'Classes: <b>{nc_display}</b> · '
62
+ f'Ep: <b>{st.session_state.get("epochs", TRAIN_EPOCHS)}</b> · '
63
+ f'BS: <b>{st.session_state.get("batch_size", TRAIN_BATCH_SIZE)}</b> · '
64
+ f'LR: <b>{st.session_state.get("lr", TRAIN_LR):.1e}</b> · '
65
+ f'Img: <b>{st.session_state.get("img_size", TRAIN_IMG_SIZE)}</b> · '
66
+ f'AMP: <b>{"On" if st.session_state.get("amp", True) else "Off"}</b>'
67
+ f'</div>',
68
+ unsafe_allow_html=True,
69
+ )
70
+ with st.expander("Save / Load Config", expanded=False):
71
+ c1, c2 = st.columns(2)
72
+ with c1:
73
+ from flashstudio.utils.config import build_training_config
74
+ full_cfg = build_training_config()
75
+ st.download_button("Download YAML", yaml.dump(full_cfg, default_flow_style=False, sort_keys=False),
76
+ file_name="config.yaml", mime="text/yaml", use_container_width=True)
77
+ with c2:
78
+ up = st.file_uploader("Load", type=["yaml", "yml"], key="cfg_upload", label_visibility="collapsed")
79
+ if up:
80
+ try:
81
+ loaded = yaml.safe_load(up.read().decode("utf-8"))
82
+ if st.button("Apply", key="apply_cfg", use_container_width=True):
83
+ from flashstudio.utils import flash
84
+ from flashstudio.pages.model.utils.config import _apply_cfg
85
+ _apply_cfg(loaded)
86
+ flash("Config applied", "success")
87
+ st.rerun()
88
+ except Exception as e:
89
+ st.error(str(e)[:40])
@@ -0,0 +1,30 @@
1
+ """FlashStudio — Model Config (ultra-compact, no-scroll)."""
2
+
3
+ import streamlit as st
4
+
5
+ from flashstudio.pages.model.architecture.tab import _tab_arch
6
+ from flashstudio.pages.model.hyperparams.tab import _tab_hyper, _summary_bar
7
+ from flashstudio.pages.model.augment.tab import _tab_aug
8
+ from flashstudio.pages.model.advanced.tab import _tab_adv
9
+
10
+
11
+ def render_model_page():
12
+ from flashstudio.components.styles import render_page_header
13
+ from flashstudio.utils import show_flashes, update_config_mirror
14
+ render_page_header("", "Model")
15
+ show_flashes()
16
+
17
+ tab_arch, tab_hyper, tab_aug, tab_adv = st.tabs(["Architecture", "Hyperparams", "Augment", "Advanced"])
18
+
19
+ with tab_arch:
20
+ _tab_arch()
21
+ with tab_hyper:
22
+ _tab_hyper()
23
+ with tab_aug:
24
+ _tab_aug()
25
+ with tab_adv:
26
+ _tab_adv()
27
+
28
+ _summary_bar()
29
+
30
+ update_config_mirror()
@@ -0,0 +1,3 @@
1
+ """Model utility helpers."""
2
+
3
+ from flashstudio.pages.model.utils.config import _build_cfg, _apply_cfg # noqa: F401