flashdetect-cu124 1.0.0__py3-none-win_amd64.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.
@@ -0,0 +1,339 @@
1
+ """
2
+ FlashDetect Python Wrapper — safe, Pythonic API on top of flashdetect.dll
3
+ """
4
+ import ctypes
5
+ import os
6
+ import glob
7
+ from typing import List, Optional, Tuple
8
+
9
+ try:
10
+ import tensorrt_libs
11
+ os.add_dll_directory(tensorrt_libs.__path__[0])
12
+ except ImportError:
13
+ pass
14
+ _nvidia_base = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'nvidia')
15
+ if os.path.isdir(_nvidia_base):
16
+ for _root, _dirs, _files in os.walk(_nvidia_base):
17
+ if any(f.endswith('.dll') for f in _files):
18
+ os.add_dll_directory(_root)
19
+
20
+
21
+ class Detection:
22
+ """Single detection result."""
23
+ __slots__ = ("x1", "y1", "x2", "y2", "conf", "class_id")
24
+
25
+ def __init__(self, x1: float, y1: float, x2: float, y2: float,
26
+ conf: float, class_id: int):
27
+ self.x1 = x1
28
+ self.y1 = y1
29
+ self.x2 = x2
30
+ self.y2 = y2
31
+ self.conf = conf
32
+ self.class_id = class_id
33
+
34
+ def __repr__(self):
35
+ return (f"Detection(x1={self.x1:.0f}, y1={self.y1:.0f}, "
36
+ f"x2={self.x2:.0f}, y2={self.y2:.0f}, "
37
+ f"cls={self.class_id}, conf={self.conf:.2f})")
38
+
39
+ @property
40
+ def xyxy(self) -> Tuple[float, float, float, float]:
41
+ return (self.x1, self.y1, self.x2, self.y2)
42
+
43
+
44
+ class FlashDetect:
45
+ """
46
+ Low-latency YOLO26 inference engine.
47
+ """
48
+
49
+ def __init__(
50
+ self,
51
+ engine_path: str,
52
+ conf: float = 0.25,
53
+ device_id: int = 0,
54
+ target_classes: Optional[List[int]] = None,
55
+ max_dets: int = 0,
56
+ format: str = "BGR",
57
+ resize_mode: int = 0, # 0=no resize (default), 1=GPU resize
58
+ src_w: int = 0, # source width (resize_mode=1)
59
+ src_h: int = 0, # source height (resize_mode=1)
60
+ sdk_dir: Optional[str] = None,
61
+ ):
62
+ if sdk_dir is None:
63
+ # Auto-detect: look for flashdetect.dll next to this file
64
+ sdk_dir = os.path.dirname(os.path.abspath(__file__))
65
+ # Also try parent directory's runtime folder
66
+ parent_runtime = os.path.join(os.path.dirname(sdk_dir), "runtime")
67
+ if os.path.isdir(parent_runtime):
68
+ os.add_dll_directory(parent_runtime)
69
+ os.add_dll_directory(sdk_dir)
70
+
71
+ # Find DLL
72
+ dll_path = os.path.join(sdk_dir, "flashdetect.dll")
73
+ if not os.path.exists(dll_path):
74
+ raise FileNotFoundError(f"flashdetect.dll not found in {sdk_dir}")
75
+
76
+ try:
77
+ self._dll = ctypes.CDLL(dll_path)
78
+ except OSError as e:
79
+ raise RuntimeError(
80
+ f"Cannot load flashdetect.dll — missing runtime DLLs.\n"
81
+ f" {e}\n"
82
+ f" Ensure CUDA 12.4 + TensorRT 11.0 are installed."
83
+ ) from e
84
+ self._setup_ctypes()
85
+
86
+ # Resolve engine path (support glob)
87
+ if not os.path.exists(engine_path):
88
+ candidates = glob.glob(engine_path)
89
+ if not candidates:
90
+ candidates = glob.glob(os.path.join(os.path.dirname(sdk_dir), "*.engine"))
91
+ if candidates:
92
+ engine_path = candidates[0]
93
+ else:
94
+ raise FileNotFoundError(f"Engine not found: {engine_path}")
95
+
96
+ # Prepare target classes
97
+ n_labels = len(target_classes) if target_classes else 0
98
+ c_labels = (ctypes.c_int * n_labels)(*target_classes) if target_classes else None
99
+
100
+ # Convert format string to DLL value
101
+ fmt_map = {"BGR": 0, "RGB": 1}
102
+ fmt_val = fmt_map.get(format.upper(), 0)
103
+
104
+ # Create context
105
+ self._ctx = self._dll.fd_create(
106
+ engine_path.encode("utf-8"),
107
+ ctypes.c_float(conf),
108
+ ctypes.c_int(device_id),
109
+ c_labels,
110
+ ctypes.c_int(n_labels),
111
+ ctypes.c_int(max_dets),
112
+ ctypes.c_int(fmt_val),
113
+ ctypes.c_int(resize_mode),
114
+ ctypes.c_int(src_w),
115
+ ctypes.c_int(src_h),
116
+ )
117
+ if not self._ctx:
118
+ # Try to get machine ID to guide the user
119
+ try:
120
+ mid = get_machine_id()
121
+ except Exception:
122
+ mid = "<unavailable>"
123
+ raise RuntimeError(
124
+ f"fd_create failed — likely missing or invalid license.key\n"
125
+ f" Your machine ID: {mid}\n"
126
+ f" Send this ID to the vendor to obtain a license.key file,\n"
127
+ f" then place it beside flashdetect.dll.\n"
128
+ f" Or run: python -c \"import flashdetect; print(flashdetect.get_machine_id())\""
129
+ )
130
+
131
+ # Get engine input size
132
+ h, w = ctypes.c_int(), ctypes.c_int()
133
+ self._dll.fd_get_size(self._ctx, ctypes.byref(h), ctypes.byref(w))
134
+ self.input_height = h.value
135
+ self.input_width = w.value
136
+ self.resize_mode = resize_mode
137
+ self._src_w = src_w if src_w > 0 else self.input_width
138
+ self._src_h = src_h if src_h > 0 else self.input_height
139
+
140
+ # ── ctypes setup ──────────────────────────────
141
+
142
+ def _setup_ctypes(self):
143
+ d = self._dll
144
+
145
+ class _Ctx(ctypes.Structure):
146
+ pass
147
+
148
+ class _Det(ctypes.Structure):
149
+ _fields_ = [
150
+ ("x1", ctypes.c_float), ("y1", ctypes.c_float),
151
+ ("x2", ctypes.c_float), ("y2", ctypes.c_float),
152
+ ("conf", ctypes.c_float), ("class_id", ctypes.c_int),
153
+ ]
154
+
155
+ d.fd_create.argtypes = [
156
+ ctypes.c_char_p, ctypes.c_float, ctypes.c_int,
157
+ ctypes.POINTER(ctypes.c_int), ctypes.c_int, ctypes.c_int, ctypes.c_int,
158
+ ctypes.c_int, ctypes.c_int, ctypes.c_int,
159
+ ]
160
+ d.fd_create.restype = ctypes.POINTER(_Ctx)
161
+
162
+ d.fd_process.argtypes = [
163
+ ctypes.POINTER(_Ctx), ctypes.POINTER(ctypes.c_uint8),
164
+ ctypes.POINTER(_Det), ctypes.c_int, ctypes.POINTER(ctypes.c_int),
165
+ ]
166
+ d.fd_process.restype = ctypes.c_int
167
+
168
+ d.fd_get_size.argtypes = [
169
+ ctypes.POINTER(_Ctx), ctypes.POINTER(ctypes.c_int),
170
+ ctypes.POINTER(ctypes.c_int),
171
+ ]
172
+ d.fd_get_size.restype = None
173
+
174
+ d.fd_release.argtypes = [ctypes.POINTER(_Ctx)]
175
+ d.fd_release.restype = None
176
+
177
+ d.fd_set_conf.argtypes = [ctypes.POINTER(_Ctx), ctypes.c_float]
178
+ d.fd_set_conf.restype = None
179
+
180
+ d.fd_set_classes.argtypes = [ctypes.POINTER(_Ctx), ctypes.POINTER(ctypes.c_int), ctypes.c_int]
181
+ d.fd_set_classes.restype = None
182
+
183
+ self._Det = _Det
184
+
185
+ # ── Public API ────────────────────────────────
186
+
187
+ def detect(self, image_rgb, conf: float = None, classes: List[int] = None) -> List[Detection]:
188
+ """
189
+ Run inference on a single image.
190
+
191
+ Args:
192
+ image_rgb: numpy array (H, W, 3) uint8 RGB.
193
+ conf: Override confidence threshold for this frame (None = keep current).
194
+ classes: Override target classes for this frame (None = keep current).
195
+
196
+ Returns:
197
+ List of Detection objects.
198
+ """
199
+ import numpy as np
200
+ if not isinstance(image_rgb, np.ndarray):
201
+ raise TypeError("image_rgb must be a numpy array")
202
+ if image_rgb.dtype != np.uint8:
203
+ image_rgb = image_rgb.astype(np.uint8)
204
+ if not image_rgb.flags["C_CONTIGUOUS"]:
205
+ image_rgb = np.ascontiguousarray(image_rgb)
206
+
207
+ if conf is not None:
208
+ self._dll.fd_set_conf(self._ctx, ctypes.c_float(conf))
209
+ if classes is not None:
210
+ n = len(classes)
211
+ c_cls = (ctypes.c_int * n)(*classes)
212
+ self._dll.fd_set_classes(self._ctx, c_cls, ctypes.c_int(n))
213
+
214
+ dets = (self._Det * 300)()
215
+ count = ctypes.c_int()
216
+ ptr = image_rgb.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8))
217
+ ret = self._dll.fd_process(self._ctx, ptr, dets, 300, ctypes.byref(count))
218
+ if ret != 0:
219
+ raise RuntimeError(f"fd_process failed with code {ret}")
220
+
221
+ results = []
222
+ sx = self._src_w / self.input_width if self.resize_mode else 1.0
223
+ sy = self._src_h / self.input_height if self.resize_mode else 1.0
224
+ for i in range(count.value):
225
+ d = dets[i]
226
+ b = Detection(d.x1*sx, d.y1*sy, d.x2*sx, d.y2*sy, d.conf, d.class_id)
227
+ results.append(b)
228
+ return results
229
+
230
+ def __call__(self, image) -> List[Detection]:
231
+ """Shortcut: engine(image) → detections"""
232
+ return self.detect(image)
233
+
234
+ def close(self):
235
+ """Release GPU resources."""
236
+ if self._ctx:
237
+ self._dll.fd_release(self._ctx)
238
+ self._ctx = None
239
+
240
+ def __enter__(self):
241
+ return self
242
+
243
+ def __exit__(self, *args):
244
+ self.close()
245
+
246
+ def __del__(self):
247
+ """Auto-release on garbage collection."""
248
+ try:
249
+ self.close()
250
+ except Exception:
251
+ pass
252
+
253
+ @property
254
+ def input_size(self) -> Tuple[int, int]:
255
+ return (self.input_height, self.input_width)
256
+
257
+
258
+ # ── Module-level utilities ──────────────────────
259
+
260
+ def get_machine_id(sdk_dir: str = None) -> str:
261
+ """
262
+ Get this machine's unique hardware ID for license generation.
263
+
264
+ Usage (first-time setup):
265
+ >>> import flashdetect
266
+ >>> print(flashdetect.get_machine_id())
267
+ A1B2C3D4E5F67890
268
+
269
+ Send this ID to the vendor to receive a license.key file,
270
+ then place it beside flashdetect.dll (in the same directory).
271
+
272
+ Args:
273
+ sdk_dir: Path to the directory containing flashdetect.dll.
274
+ Defaults to the package install directory.
275
+ Returns:
276
+ 16-character hex machine ID string.
277
+ """
278
+ if sdk_dir is None:
279
+ sdk_dir = os.path.dirname(os.path.abspath(__file__))
280
+ os.add_dll_directory(sdk_dir)
281
+
282
+ dll_path = os.path.join(sdk_dir, "flashdetect.dll")
283
+ if not os.path.exists(dll_path):
284
+ raise FileNotFoundError(f"flashdetect.dll not found in {sdk_dir}")
285
+
286
+ try:
287
+ dll = ctypes.CDLL(dll_path)
288
+ except OSError as e:
289
+ raise RuntimeError(
290
+ f"Cannot load flashdetect.dll — missing runtime DLLs.\n"
291
+ f" {e}\n"
292
+ f" Ensure CUDA 12.4 + TensorRT 11.0 are installed."
293
+ ) from e
294
+ dll.fd_get_machine_id.argtypes = [ctypes.c_char_p, ctypes.c_int]
295
+ dll.fd_get_machine_id.restype = ctypes.c_int
296
+
297
+ buf = ctypes.create_string_buffer(32)
298
+ if dll.fd_get_machine_id(buf, 32) != 0:
299
+ raise RuntimeError("Failed to get machine ID")
300
+ return buf.value.decode("utf-8")
301
+
302
+
303
+ def install_license(source_path: str, sdk_dir: str = None) -> str:
304
+ """
305
+ Copy a license.key file into the flashdetect package directory.
306
+
307
+ Usage:
308
+ >>> import flashdetect
309
+ >>> flashdetect.install_license("C:/Users/me/Downloads/license.key")
310
+
311
+ This copies the file to the same directory as flashdetect.dll, where
312
+ the DLL looks for it at runtime.
313
+
314
+ Args:
315
+ source_path: Path to your license.key file.
316
+ sdk_dir: Target directory (default: flashdetect package dir).
317
+ Returns:
318
+ The destination path where license.key was installed.
319
+ """
320
+ import shutil
321
+
322
+ if not os.path.exists(source_path):
323
+ raise FileNotFoundError(f"license.key not found at: {source_path}")
324
+
325
+ if sdk_dir is None:
326
+ sdk_dir = os.path.dirname(os.path.abspath(__file__))
327
+
328
+ dest_path = os.path.join(sdk_dir, "license.key")
329
+
330
+ if os.path.exists(dest_path):
331
+ print(f"[flashdetect] Replacing existing: {dest_path}")
332
+
333
+ shutil.copy2(source_path, dest_path)
334
+ print(f"[flashdetect] license.key installed → {dest_path}")
335
+
336
+ mid = get_machine_id(sdk_dir)
337
+ print(f"[flashdetect] Machine ID: {mid}")
338
+
339
+ return dest_path
Binary file
@@ -0,0 +1,150 @@
1
+ Metadata-Version: 2.4
2
+ Name: flashdetect-cu124
3
+ Version: 1.0.0
4
+ Summary: Low-latency YOLO26 TensorRT inference for real-time video streams
5
+ Home-page: https://github.com/toever/flashdetect
6
+ Classifier: Development Status :: 3 - Alpha
7
+ Classifier: Intended Audience :: Developers
8
+ Classifier: License :: Other/Proprietary License
9
+ Classifier: Operating System :: Microsoft :: Windows
10
+ Classifier: Environment :: GPU :: NVIDIA CUDA
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
14
+ Requires-Python: >=3.8
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: numpy>=1.21
17
+ Requires-Dist: tensorrt-cu12
18
+ Requires-Dist: nvidia-cuda-runtime-cu12
19
+ Requires-Dist: nvidia-cublas-cu12
20
+ Dynamic: classifier
21
+ Dynamic: description
22
+ Dynamic: description-content-type
23
+ Dynamic: home-page
24
+ Dynamic: requires-dist
25
+ Dynamic: requires-python
26
+ Dynamic: summary
27
+
28
+ # FlashDetect
29
+
30
+ **低延迟 YOLO26 目标检测推理引擎,专为实时视频流优化。仅支持 YOLO26 端到端导出(`[N,6]` 格式,无 NMS)。**
31
+
32
+ 基于 TensorRT + CUDA Graph,全 GPU 流水线。
33
+
34
+ ## 安装
35
+
36
+ ```bash
37
+ pip install flashdetect-cu124
38
+ ```
39
+
40
+ ### 系统要求
41
+
42
+ - Windows x64
43
+ - NVIDIA GPU (Compute Capability ≥ 7.0)
44
+ - Python ≥ 3.8
45
+
46
+ 运行时依赖自动安装(CUDA 12.4 + TensorRT 11.x)。
47
+
48
+ ## 激活授权
49
+
50
+ 本 SDK 需要硬件绑定授权,首次使用:
51
+
52
+ ```python
53
+ import flashdetect
54
+
55
+ # 1. 获取本机 ID,发送给供应商
56
+ print(flashdetect.get_machine_id())
57
+
58
+ # 2. 收到 license.key 后,一键安装
59
+ flashdetect.install_license("C:/Users/me/Downloads/license.key")
60
+ ```
61
+
62
+ 激活后即可正常使用。
63
+
64
+ ## 快速开始
65
+
66
+ ```python
67
+ from flashdetect import FlashDetect
68
+
69
+ # 加载模型(支持 with 语句自动释放)
70
+ with FlashDetect("yolo26n.engine", conf=0.25) as detector:
71
+ frame = camera.read() # (H, W, 3) BGR uint8
72
+ dets = detector.detect(frame)
73
+
74
+ for d in dets:
75
+ print(f" cls={d.class_id} conf={d.conf:.2f} "
76
+ f"({d.x1:.0f},{d.y1:.0f})-({d.x2:.0f},{d.y2:.0f})")
77
+ ```
78
+
79
+ ## API 参考
80
+
81
+ ### 模块级工具
82
+
83
+ | 函数 | 说明 |
84
+ |------|------|
85
+ | `flashdetect.get_machine_id()` | 获取本机硬件 ID(用于申请授权) |
86
+ | `flashdetect.install_license(path)` | 安装 license.key 到正确位置 |
87
+
88
+ ### `FlashDetect(engine_path, **kwargs)`
89
+
90
+ | 参数 | 类型 | 默认值 | 说明 |
91
+ |------|------|--------|------|
92
+ | `engine_path` | `str` | 必填 | TensorRT .engine 文件路径 |
93
+ | `conf` | `float` | `0.25` | 置信度阈值 |
94
+ | `device_id` | `int` | `0` | GPU 设备编号 |
95
+ | `target_classes` | `List[int]` | `None` | 只检测指定类别(None=全部) |
96
+ | `max_dets` | `int` | `0` | 最大检测数(0=引擎默认) |
97
+ | `format` | `str` | `"BGR"` | 输入颜色格式:`"BGR"` 或 `"RGB"` |
98
+ | `resize_mode` | `int` | `0` | 0=不缩放,1=GPU 缩放至引擎尺寸 |
99
+ | `src_w` | `int` | `0` | 源图像宽度(resize_mode=1 时必填) |
100
+ | `src_h` | `int` | `0` | 源图像高度(resize_mode=1 时必填) |
101
+
102
+ ### `detect(image, conf=None, classes=None) -> List[Detection]`
103
+
104
+ | 参数 | 类型 | 说明 |
105
+ |------|------|------|
106
+ | `image` | `np.ndarray` | (H, W, 3) uint8 图像 |
107
+ | `conf` | `float` | 可选,覆盖当前帧的置信度阈值 |
108
+ | `classes` | `List[int]` | 可选,覆盖当前帧的目标类别 |
109
+
110
+ ### `Detection`
111
+
112
+ | 属性 | 类型 | 说明 |
113
+ |------|------|------|
114
+ | `x1, y1, x2, y2` | `float` | 边界框坐标 |
115
+ | `conf` | `float` | 置信度 |
116
+ | `class_id` | `int` | 类别 ID |
117
+ | `xyxy` | `tuple` | `(x1, y1, x2, y2)` 快捷属性 |
118
+
119
+ ### 其他方法
120
+
121
+ ```python
122
+ detector.input_width, detector.input_height # 引擎输入尺寸
123
+ detector.close() # 释放 GPU 资源
124
+ # 或使用 with 语句自动释放:
125
+ # with FlashDetect("model.engine") as detector:
126
+ # dets = detector.detect(frame)
127
+ ```
128
+
129
+ ## 性能
130
+
131
+ 待测试
132
+
133
+ CUDA Graph 确保每次推理耗时稳定,无 CPU 端开销。
134
+
135
+ ## 适用场景
136
+
137
+ - 游戏画面实时分析(DX 采集)
138
+ - 工业相机检测
139
+ - 视频流目标跟踪
140
+
141
+ **不适用**:图片文件批量识别(无需 GPU Graph 优化)。
142
+
143
+ ## 许可 & 联系
144
+
145
+ 本 SDK 需硬件绑定授权,每台机器独立 `license.key`。
146
+ 无有效授权时,`FlashDetect()` 会抛出异常并显示本机 ID。
147
+
148
+ 获取授权请联系:
149
+
150
+ - 📧 邮箱:`2169431623@qq.com` / `wut815318@gmail.com`
@@ -0,0 +1,6 @@
1
+ flashdetect/__init__.py,sha256=p7JADJvCDj5NTh67tdFUdEhoIxy69v7TmcWH4bNwIjo,12164
2
+ flashdetect/flashdetect.dll,sha256=yFXtV5sTJLBT7avHYP3TVXDUfkVyR2QE-jTBZ51jdb4,78336
3
+ flashdetect_cu124-1.0.0.dist-info/METADATA,sha256=O-arKuIHRSelAPZdehUNht2cImQNLQzKAbIRNFF4NMQ,4567
4
+ flashdetect_cu124-1.0.0.dist-info/WHEEL,sha256=QR8DNjG6Lr6bNErJWJgF4dP2dJ2N7NpY-BWly1OvcTM,97
5
+ flashdetect_cu124-1.0.0.dist-info/top_level.txt,sha256=FCzr5dABchxs3JZZRINKHH6sKMM5s1w-K5rHy3MLy5E,12
6
+ flashdetect_cu124-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-win_amd64
5
+
@@ -0,0 +1 @@
1
+ flashdetect