ultralytics
Eval Results
bayramsn commited on
Commit
8c738c4
·
1 Parent(s): 4839c43

feat(tools): add pt_bundle to bundle multiple .pt files without modifying originals\nfix(apps): webcam_app device auto-resolution and session_state safety

Browse files
Files changed (3) hide show
  1. apps/README.md +29 -0
  2. apps/webcam_app.py +155 -0
  3. tools/pt_bundle.py +159 -0
apps/README.md ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # YOLO11 Webcam App
2
+
3
+ Basit bir Streamlit arayüzü ile webcam üzerinden YOLO11 gerçek zamanlı tespit.
4
+
5
+ ## Gereksinimler
6
+ - Python 3.9+
7
+ - Paketler: `ultralytics`, `streamlit`, `streamlit-webrtc`, `av`, (opsiyonel) `huggingface_hub`
8
+
9
+ ## Kurulum (PowerShell)
10
+ ```powershell
11
+ cd C:\yolov11\YOLO11
12
+ python -m venv .venv
13
+ . .venv\Scripts\Activate.ps1
14
+ pip install -U pip
15
+ pip install ultralytics streamlit streamlit-webrtc av huggingface_hub
16
+ ```
17
+
18
+ ## Çalıştırma
19
+ ```powershell
20
+ cd C:\yolov11\YOLO11
21
+ . .venv\Scripts\Activate.ps1
22
+ streamlit run apps/webcam_app.py
23
+ ```
24
+
25
+ Sol menüden modeli yükleyin (örn. `yolo11n.pt`) ve kameraya izin verin.
26
+
27
+ Notlar:
28
+ - HF Hub’dan özel model çekmek için `HF_TOKEN` ortam değişkeni gerekebilir.
29
+ - CUDA yoksa cihazı `cpu` seçin.
apps/webcam_app.py ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ from typing import Optional
4
+
5
+ import av
6
+ import torch
7
+ import streamlit as st
8
+ from streamlit_webrtc import webrtc_streamer, VideoProcessorBase, RTCConfiguration
9
+
10
+ try:
11
+ from ultralytics import YOLO
12
+ except Exception as e:
13
+ st.error(f"Ultralytics import failed: {e}")
14
+ raise
15
+
16
+
17
+ st.set_page_config(page_title="YOLO11 Webcam", layout="wide")
18
+ st.title("YOLO11 Webcam Demo")
19
+ st.caption("Live object detection with YOLO11 using your webcam. Use the sidebar to configure the model and thresholds.")
20
+
21
+
22
+ def _resolve_device(device: str) -> str:
23
+ """Map UI device option to a valid torch/Ultralytics device string."""
24
+ d = (device or "").lower().strip()
25
+ if d in {"cpu", "cuda", "mps", "xpu"}:
26
+ return d
27
+ if d == "auto":
28
+ if torch.cuda.is_available():
29
+ return "cuda"
30
+ # Apple MPS (macOS). Kept for completeness even if not typical on Windows.
31
+ if hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
32
+ return "mps"
33
+ return "cpu"
34
+ # Fallback to CPU for any unknown value
35
+ return "cpu"
36
+
37
+
38
+ @st.cache_resource(show_spinner=True)
39
+ def load_model(model_source: str, device: str = "auto"):
40
+ """
41
+ Load an Ultralytics model from a local path, built-in alias, or Hugging Face Hub path.
42
+ Examples for model_source:
43
+ - "yolo11n.pt" (auto-downloads if available)
44
+ - "C:/path/to/your_model.pt"
45
+ - "hf://Ultralytics/YOLO11/yolo11n.pt" (HF Hub file)
46
+ """
47
+ # Resolve device (maps 'auto' to an actual device)
48
+ resolved_device = _resolve_device(device)
49
+
50
+ # Hugging Face hub-style path
51
+ if model_source.startswith("hf://"):
52
+ try:
53
+ from huggingface_hub import hf_hub_download
54
+ except Exception as e:
55
+ st.error("huggingface_hub not installed. Install it or use a local/builtin model.")
56
+ raise
57
+
58
+ # Parse hf path: hf://<repo_id>/<filename>
59
+ path = model_source.replace("hf://", "", 1)
60
+ if "/" not in path:
61
+ raise ValueError("For hf://, use hf://<repo_id>/<filename>")
62
+ repo_id, filename = path.split("/", 1)
63
+ st.info(f"Downloading {filename} from {repo_id}...")
64
+ local_path = hf_hub_download(repo_id=repo_id, filename=filename, token=os.getenv("HF_TOKEN"))
65
+ try:
66
+ return YOLO(local_path).to(resolved_device)
67
+ except Exception as e:
68
+ st.warning(f"{resolved_device} cihaza taşınamadı, CPU'ya düşülüyor. Detay: {e}")
69
+ return YOLO(local_path).to("cpu")
70
+
71
+ # Local file or alias handled directly by Ultralytics
72
+ try:
73
+ return YOLO(model_source).to(resolved_device)
74
+ except Exception as e:
75
+ st.warning(f"{resolved_device} cihaza taşınamadı, CPU'ya düşülüyor. Detay: {e}")
76
+ return YOLO(model_source).to("cpu")
77
+
78
+
79
+ class YOLOProcessor(VideoProcessorBase):
80
+ def __init__(self, model: YOLO, conf: float, iou: float):
81
+ self.model = model
82
+ self.conf = conf
83
+ self.iou = iou
84
+
85
+ def recv(self, frame: av.VideoFrame) -> av.VideoFrame:
86
+ img = frame.to_ndarray(format="bgr24")
87
+ results = self.model.predict(img, conf=self.conf, iou=self.iou, verbose=False)
88
+ plotted = results[0].plot()
89
+ return av.VideoFrame.from_ndarray(plotted, format="bgr24")
90
+
91
+
92
+ with st.sidebar:
93
+ st.header("Ayarlar")
94
+ default_model = "yolo11n.pt" # Ultralytics dağıtımından otomatik indirilmeye çalışılır
95
+ model_path = st.text_input("Model yolu veya alias", value=default_model, help="Yerel .pt yolu, yerleşik alias (örn. yolo11n.pt) veya hf://Ultralytics/YOLO11/yolo11n.pt")
96
+ device = st.selectbox("Cihaz", options=["auto", "cpu", "cuda"], index=0)
97
+ conf = st.slider("Confidence", min_value=0.1, max_value=0.9, value=0.25, step=0.05)
98
+ iou = st.slider("IoU", min_value=0.1, max_value=0.9, value=0.45, step=0.05)
99
+ load_btn = st.button("Modeli Yükle")
100
+
101
+
102
+ st.session_state.setdefault("model", None)
103
+
104
+
105
+ if load_btn:
106
+ try:
107
+ st.session_state.model = load_model(model_path, device=device)
108
+ names = getattr(st.session_state.model, "names", None)
109
+ if isinstance(names, dict):
110
+ st.success(f"Model yüklendi. Sınıflar: {len(names)}")
111
+ else:
112
+ st.success("Model yüklendi.")
113
+ except Exception as e:
114
+ st.exception(e)
115
+
116
+
117
+ rtc_config = RTCConfiguration({
118
+ "iceServers": [{"urls": ["stun:stun.l.google.com:19302"]}],
119
+ })
120
+
121
+
122
+ col1, col2 = st.columns([2, 1])
123
+ with col1:
124
+ st.subheader("Webcam")
125
+ if st.session_state.get("model") is None:
126
+ st.info("Önce soldan bir model yükleyin.")
127
+ else:
128
+ # Capture current values to avoid session_state race during worker creation
129
+ _model = st.session_state.get("model")
130
+ _conf = float(conf)
131
+ _iou = float(iou)
132
+
133
+ def video_processor_factory(model=_model, conf_val=_conf, iou_val=_iou):
134
+ return YOLOProcessor(model, conf=conf_val, iou=iou_val)
135
+
136
+ webrtc_streamer(
137
+ key="yolo11-webcam",
138
+ video_processor_factory=video_processor_factory,
139
+ rtc_configuration=rtc_config,
140
+ media_stream_constraints={"video": True, "audio": False},
141
+ )
142
+
143
+ with col2:
144
+ st.subheader("İpuçları")
145
+ st.markdown(
146
+ """
147
+ - Model alanına şunlardan birini girebilirsiniz:
148
+ - Yerleşik: `yolo11n.pt` (veya sizde olan başka bir .pt)
149
+ - Yerel dosya: `C:/yolov11/weights/custom.pt`
150
+ - HF Hub: `hf://Ultralytics/YOLO11/yolo11n.pt`
151
+ - Hugging Face özel/korumalı dosyalar için `HF_TOKEN` ortam değişkenini ayarlayabilirsiniz.
152
+ - GPU (CUDA) yoksa cihazı `cpu` bırakabilirsiniz.
153
+ - Stream durmuyorsa tarayıcı izinlerini kontrol edin ve sayfayı yenileyin.
154
+ """
155
+ )
tools/pt_bundle.py ADDED
@@ -0,0 +1,159 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ """
3
+ PT bundler: Bundle multiple .pt files into a single archive without modifying originals.
4
+
5
+ Supports two formats:
6
+ 1) ZIP archive (recommended) – exact bytes of each .pt preserved.
7
+ 2) PT container – a single .pt (pickle) file containing a dict {relative_path: bytes}.
8
+
9
+ CLI examples (PowerShell):
10
+ # Create ZIP bundle from current repo
11
+ python tools/pt_bundle.py zip --source . --out models_bundle.zip
12
+
13
+ # Create PT container bundle
14
+ python tools/pt_bundle.py pt --source . --out models_multi.pt
15
+
16
+ # List contents
17
+ python tools/pt_bundle.py list --bundle models_bundle.zip
18
+ python tools/pt_bundle.py list --bundle models_multi.pt
19
+
20
+ # Extract a single model from bundle to a path
21
+ python tools/pt_bundle.py extract --bundle models_multi.pt --member path/to/model.pt --out C:/tmp/model.pt
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import argparse
27
+ import io
28
+ import os
29
+ import sys
30
+ from pathlib import Path
31
+ from typing import Iterable, List
32
+
33
+ try:
34
+ import torch # Only needed for PT container
35
+ except Exception: # pragma: no cover - optional for ZIP-only usage
36
+ torch = None # type: ignore
37
+
38
+ import zipfile
39
+
40
+
41
+ def find_pt_files(source: Path, include: Iterable[str] | None = None, exclude: Iterable[str] | None = None) -> List[Path]:
42
+ include = list(include or ["*.pt"]) # default include all .pt
43
+ exclude = list(exclude or [])
44
+ files: List[Path] = []
45
+ for p in source.rglob("*.pt"):
46
+ rel = p.relative_to(source)
47
+ rel_str = str(rel).replace("\\", "/")
48
+ if include and not any(Path(rel_str).match(pat) for pat in include):
49
+ continue
50
+ if exclude and any(Path(rel_str).match(pat) for pat in exclude):
51
+ continue
52
+ files.append(p)
53
+ return files
54
+
55
+
56
+ def create_zip_bundle(source: Path, out_path: Path, includes: Iterable[str] | None = None, excludes: Iterable[str] | None = None) -> int:
57
+ files = find_pt_files(source, includes, excludes)
58
+ out_path.parent.mkdir(parents=True, exist_ok=True)
59
+ with zipfile.ZipFile(out_path, mode="w", compression=zipfile.ZIP_DEFLATED) as zf:
60
+ for f in files:
61
+ zf.write(f, f.relative_to(source))
62
+ return len(files)
63
+
64
+
65
+ def create_pt_container(source: Path, out_path: Path, includes: Iterable[str] | None = None, excludes: Iterable[str] | None = None) -> int:
66
+ if torch is None:
67
+ raise RuntimeError("torch is required for PT container mode. Install torch and retry.")
68
+ files = find_pt_files(source, includes, excludes)
69
+ payload = {}
70
+ for f in files:
71
+ rel = str(f.relative_to(source)).replace("\\", "/")
72
+ with open(f, "rb") as fh:
73
+ payload[rel] = fh.read() # store exact bytes (no mutation)
74
+ out_path.parent.mkdir(parents=True, exist_ok=True)
75
+ torch.save(payload, out_path)
76
+ return len(files)
77
+
78
+
79
+ def list_bundle(bundle: Path) -> List[str]:
80
+ if bundle.suffix.lower() == ".zip":
81
+ with zipfile.ZipFile(bundle, "r") as zf:
82
+ return [i.filename for i in zf.infolist() if not i.is_dir()]
83
+ else:
84
+ if torch is None:
85
+ raise RuntimeError("torch is required to list PT container contents.")
86
+ data = torch.load(bundle, map_location="cpu")
87
+ if isinstance(data, dict):
88
+ return sorted(map(str, data.keys()))
89
+ raise ValueError("Unsupported PT container format: expected dict mapping.")
90
+
91
+
92
+ def extract_member(bundle: Path, member: str, out_path: Path) -> None:
93
+ if bundle.suffix.lower() == ".zip":
94
+ with zipfile.ZipFile(bundle, "r") as zf:
95
+ with zf.open(member, "r") as fh, open(out_path, "wb") as out:
96
+ out.write(fh.read())
97
+ else:
98
+ if torch is None:
99
+ raise RuntimeError("torch is required to extract from PT container.")
100
+ data = torch.load(bundle, map_location="cpu")
101
+ if not isinstance(data, dict):
102
+ raise ValueError("Unsupported PT container format: expected dict mapping.")
103
+ if member not in data:
104
+ raise FileNotFoundError(f"member not found in container: {member}")
105
+ out_path.parent.mkdir(parents=True, exist_ok=True)
106
+ with open(out_path, "wb") as fh:
107
+ fh.write(data[member])
108
+
109
+
110
+ def main(argv: List[str] | None = None) -> int:
111
+ parser = argparse.ArgumentParser(description="Bundle multiple .pt files without modifying originals.")
112
+ sub = parser.add_subparsers(dest="cmd", required=True)
113
+
114
+ p_zip = sub.add_parser("zip", help="Create a ZIP archive of .pt files.")
115
+ p_zip.add_argument("--source", default=".", help="Root directory to scan for .pt files.")
116
+ p_zip.add_argument("--out", required=True, help="Output ZIP path.")
117
+ p_zip.add_argument("--include", nargs="*", default=["*.pt"], help="Glob patterns to include.")
118
+ p_zip.add_argument("--exclude", nargs="*", default=[], help="Glob patterns to exclude.")
119
+
120
+ p_pt = sub.add_parser("pt", help="Create a single .pt container (dict of bytes).")
121
+ p_pt.add_argument("--source", default=".", help="Root directory to scan for .pt files.")
122
+ p_pt.add_argument("--out", required=True, help="Output PT path (e.g., models_multi.pt).")
123
+ p_pt.add_argument("--include", nargs="*", default=["*.pt"], help="Glob patterns to include.")
124
+ p_pt.add_argument("--exclude", nargs="*", default=[], help="Glob patterns to exclude.")
125
+
126
+ p_list = sub.add_parser("list", help="List contents of a bundle (ZIP or PT container).")
127
+ p_list.add_argument("--bundle", required=True, help="Path to models_bundle.zip or models_multi.pt.")
128
+
129
+ p_ext = sub.add_parser("extract", help="Extract a single member from the bundle.")
130
+ p_ext.add_argument("--bundle", required=True, help="Bundle path (ZIP or PT container).")
131
+ p_ext.add_argument("--member", required=True, help="Member path inside the bundle.")
132
+ p_ext.add_argument("--out", required=True, help="Destination file path to write.")
133
+
134
+ args = parser.parse_args(argv)
135
+
136
+ if args.cmd == "zip":
137
+ count = create_zip_bundle(Path(args.source), Path(args.out), args.include, args.exclude)
138
+ print(f"ZIP bundle written: {args.out} ({count} files)")
139
+ return 0
140
+ if args.cmd == "pt":
141
+ count = create_pt_container(Path(args.source), Path(args.out), args.include, args.exclude)
142
+ print(f"PT container written: {args.out} ({count} files)")
143
+ return 0
144
+ if args.cmd == "list":
145
+ items = list_bundle(Path(args.bundle))
146
+ for it in items:
147
+ print(it)
148
+ return 0
149
+ if args.cmd == "extract":
150
+ extract_member(Path(args.bundle), args.member, Path(args.out))
151
+ print(f"Extracted {args.member} -> {args.out}")
152
+ return 0
153
+
154
+ parser.print_help()
155
+ return 1
156
+
157
+
158
+ if __name__ == "__main__":
159
+ raise SystemExit(main())