mblt-vision-python 0.0.0__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.
Files changed (404) hide show
  1. mblt_vision/__init__.py +86 -0
  2. mblt_vision/_api.py +53 -0
  3. mblt_vision/_compat.py +227 -0
  4. mblt_vision/_model_paths.py +137 -0
  5. mblt_vision/_tasks.py +30 -0
  6. mblt_vision/benchmark/__init__.py +1 -0
  7. mblt_vision/benchmark/argparse_utils.py +106 -0
  8. mblt_vision/benchmark/chart_utils.py +146 -0
  9. mblt_vision/benchmark/io_utils.py +64 -0
  10. mblt_vision/benchmark/summary_utils.py +770 -0
  11. mblt_vision/cli/__init__.py +5 -0
  12. mblt_vision/cli/__main__.py +5 -0
  13. mblt_vision/cli/_vision.py +337 -0
  14. mblt_vision/cli/compile.py +117 -0
  15. mblt_vision/cli/main.py +35 -0
  16. mblt_vision/cli/predict.py +64 -0
  17. mblt_vision/cli/val.py +562 -0
  18. mblt_vision/compile/__init__.py +21 -0
  19. mblt_vision/compile/vision.py +1120 -0
  20. mblt_vision/datasets/__init__.py +15 -0
  21. mblt_vision/datasets/ade20k.yaml +164 -0
  22. mblt_vision/datasets/cityscapes.yaml +54 -0
  23. mblt_vision/datasets/coco.yaml +178 -0
  24. mblt_vision/datasets/dotav1.yaml +28 -0
  25. mblt_vision/datasets/imagenet.yaml +1019 -0
  26. mblt_vision/datasets/imagenet_synsets.txt +1000 -0
  27. mblt_vision/datasets/nyu-depth.yaml +7 -0
  28. mblt_vision/datasets/registry.py +158 -0
  29. mblt_vision/datasets/widerface.yaml +9 -0
  30. mblt_vision/depth_estimation/__init__.py +19 -0
  31. mblt_vision/face_detection/__init__.py +43 -0
  32. mblt_vision/image_classification/__init__.py +311 -0
  33. mblt_vision/instance_segmentation/__init__.py +65 -0
  34. mblt_vision/models/AlexNet.yaml +23 -0
  35. mblt_vision/models/CAFormer_B36.yaml +22 -0
  36. mblt_vision/models/CAFormer_M36.yaml +22 -0
  37. mblt_vision/models/CAFormer_S18.yaml +22 -0
  38. mblt_vision/models/CAFormer_S36.yaml +22 -0
  39. mblt_vision/models/CoAtNet_0_RW_224.yaml +22 -0
  40. mblt_vision/models/CoAtNet_1_RW_224.yaml +22 -0
  41. mblt_vision/models/CoAtNet_2_RW_224.yaml +22 -0
  42. mblt_vision/models/ConvFormer_B36.yaml +22 -0
  43. mblt_vision/models/ConvFormer_M36.yaml +22 -0
  44. mblt_vision/models/ConvFormer_S18.yaml +22 -0
  45. mblt_vision/models/ConvFormer_S36.yaml +22 -0
  46. mblt_vision/models/ConvNext_Base.yaml +23 -0
  47. mblt_vision/models/ConvNext_Large.yaml +23 -0
  48. mblt_vision/models/ConvNext_Small.yaml +23 -0
  49. mblt_vision/models/ConvNext_Tiny.yaml +23 -0
  50. mblt_vision/models/DeiT3_Base_Patch16_224.yaml +22 -0
  51. mblt_vision/models/DeiT3_Base_Patch16_384.yaml +22 -0
  52. mblt_vision/models/DeiT3_Large_Patch16_224.yaml +22 -0
  53. mblt_vision/models/DeiT3_Large_Patch16_384.yaml +22 -0
  54. mblt_vision/models/DeiT3_Medium_Patch16_224.yaml +22 -0
  55. mblt_vision/models/DeiT3_Small_Patch16_224.yaml +22 -0
  56. mblt_vision/models/DeiT3_Small_Patch16_384.yaml +22 -0
  57. mblt_vision/models/DeiT_Base_Patch16_224.yaml +22 -0
  58. mblt_vision/models/DeiT_Base_Patch16_384.yaml +22 -0
  59. mblt_vision/models/DeiT_Small_Patch16_224.yaml +22 -0
  60. mblt_vision/models/DeiT_Tiny_Patch16_224.yaml +22 -0
  61. mblt_vision/models/DenseNet121.yaml +23 -0
  62. mblt_vision/models/DenseNet161.yaml +23 -0
  63. mblt_vision/models/DenseNet169.yaml +23 -0
  64. mblt_vision/models/DenseNet201.yaml +23 -0
  65. mblt_vision/models/EfficientFormer_L1.yaml +22 -0
  66. mblt_vision/models/EfficientFormer_L3.yaml +22 -0
  67. mblt_vision/models/EfficientFormer_L7.yaml +22 -0
  68. mblt_vision/models/EfficientNet_B0.yaml +23 -0
  69. mblt_vision/models/EfficientNet_B1.yaml +32 -0
  70. mblt_vision/models/EfficientNet_B2.yaml +23 -0
  71. mblt_vision/models/EfficientNet_B3.yaml +23 -0
  72. mblt_vision/models/EfficientNet_B4.yaml +23 -0
  73. mblt_vision/models/EfficientNet_B5.yaml +23 -0
  74. mblt_vision/models/EfficientNet_B6.yaml +23 -0
  75. mblt_vision/models/EfficientNet_B7.yaml +23 -0
  76. mblt_vision/models/EfficientNet_V2_L.yaml +23 -0
  77. mblt_vision/models/EfficientNet_V2_M.yaml +23 -0
  78. mblt_vision/models/EfficientNet_V2_S.yaml +23 -0
  79. mblt_vision/models/FlexiVit_Base.yaml +22 -0
  80. mblt_vision/models/FlexiVit_Large.yaml +22 -0
  81. mblt_vision/models/FlexiVit_Small.yaml +22 -0
  82. mblt_vision/models/GELANc-seg.yaml +29 -0
  83. mblt_vision/models/GELANc.yaml +28 -0
  84. mblt_vision/models/GELANe.yaml +28 -0
  85. mblt_vision/models/GELANm.yaml +28 -0
  86. mblt_vision/models/GELANs.yaml +28 -0
  87. mblt_vision/models/GoogLeNet.yaml +23 -0
  88. mblt_vision/models/Inception_V3.yaml +23 -0
  89. mblt_vision/models/LeViT_Conv_128.yaml +22 -0
  90. mblt_vision/models/LeViT_Conv_128S.yaml +22 -0
  91. mblt_vision/models/LeViT_Conv_192.yaml +22 -0
  92. mblt_vision/models/LeViT_Conv_256.yaml +22 -0
  93. mblt_vision/models/LeViT_Conv_384.yaml +22 -0
  94. mblt_vision/models/MNASNet0_5.yaml +23 -0
  95. mblt_vision/models/MNASNet0_75.yaml +23 -0
  96. mblt_vision/models/MNASNet1_0.yaml +23 -0
  97. mblt_vision/models/MNASNet1_3.yaml +23 -0
  98. mblt_vision/models/MobileNet_V2.yaml +32 -0
  99. mblt_vision/models/MobileNet_V3_Large.yaml +32 -0
  100. mblt_vision/models/MobileNet_V3_Small.yaml +23 -0
  101. mblt_vision/models/RegNet_X_16GF.yaml +32 -0
  102. mblt_vision/models/RegNet_X_1_6GF.yaml +32 -0
  103. mblt_vision/models/RegNet_X_32GF.yaml +32 -0
  104. mblt_vision/models/RegNet_X_3_2GF.yaml +32 -0
  105. mblt_vision/models/RegNet_X_400MF.yaml +32 -0
  106. mblt_vision/models/RegNet_X_800MF.yaml +32 -0
  107. mblt_vision/models/RegNet_X_8GF.yaml +32 -0
  108. mblt_vision/models/RegNet_Y_16GF.yaml +76 -0
  109. mblt_vision/models/RegNet_Y_1_6GF.yaml +32 -0
  110. mblt_vision/models/RegNet_Y_32GF.yaml +76 -0
  111. mblt_vision/models/RegNet_Y_3_2GF.yaml +32 -0
  112. mblt_vision/models/RegNet_Y_400MF.yaml +32 -0
  113. mblt_vision/models/RegNet_Y_800MF.yaml +32 -0
  114. mblt_vision/models/RegNet_Y_8GF.yaml +32 -0
  115. mblt_vision/models/RepViT_M0_9.yaml +22 -0
  116. mblt_vision/models/RepViT_M1.yaml +22 -0
  117. mblt_vision/models/RepViT_M1_0.yaml +22 -0
  118. mblt_vision/models/RepViT_M1_1.yaml +22 -0
  119. mblt_vision/models/RepViT_M1_5.yaml +22 -0
  120. mblt_vision/models/RepViT_M2.yaml +22 -0
  121. mblt_vision/models/RepViT_M2_3.yaml +22 -0
  122. mblt_vision/models/RepViT_M3.yaml +22 -0
  123. mblt_vision/models/ResNeXt101_32X8D.yaml +32 -0
  124. mblt_vision/models/ResNeXt101_64X4D.yaml +23 -0
  125. mblt_vision/models/ResNeXt50_32X4D.yaml +32 -0
  126. mblt_vision/models/ResNet101.yaml +32 -0
  127. mblt_vision/models/ResNet152.yaml +32 -0
  128. mblt_vision/models/ResNet18.yaml +23 -0
  129. mblt_vision/models/ResNet34.yaml +23 -0
  130. mblt_vision/models/ResNet50.yaml +32 -0
  131. mblt_vision/models/ShuffleNet_V2_X0_5.yaml +23 -0
  132. mblt_vision/models/ShuffleNet_V2_X1_0.yaml +23 -0
  133. mblt_vision/models/ShuffleNet_V2_X1_5.yaml +23 -0
  134. mblt_vision/models/ShuffleNet_V2_X2_0.yaml +23 -0
  135. mblt_vision/models/SqueezeNet1_0.yaml +23 -0
  136. mblt_vision/models/SqueezeNet1_1.yaml +23 -0
  137. mblt_vision/models/Swin_B.yaml +23 -0
  138. mblt_vision/models/Swin_S.yaml +23 -0
  139. mblt_vision/models/Swin_T.yaml +23 -0
  140. mblt_vision/models/VGG11.yaml +23 -0
  141. mblt_vision/models/VGG11_BN.yaml +23 -0
  142. mblt_vision/models/VGG13.yaml +23 -0
  143. mblt_vision/models/VGG13_BN.yaml +23 -0
  144. mblt_vision/models/VGG16.yaml +23 -0
  145. mblt_vision/models/VGG16_BN.yaml +23 -0
  146. mblt_vision/models/VGG19.yaml +23 -0
  147. mblt_vision/models/VGG19_BN.yaml +23 -0
  148. mblt_vision/models/ViT_B_16.yaml +67 -0
  149. mblt_vision/models/ViT_B_32.yaml +23 -0
  150. mblt_vision/models/ViT_Base_Patch16_224.yaml +22 -0
  151. mblt_vision/models/ViT_Base_Patch16_384.yaml +22 -0
  152. mblt_vision/models/ViT_Base_Patch32_224.yaml +22 -0
  153. mblt_vision/models/ViT_Base_Patch32_384.yaml +22 -0
  154. mblt_vision/models/ViT_Base_Patch8_224.yaml +22 -0
  155. mblt_vision/models/ViT_L_16.yaml +67 -0
  156. mblt_vision/models/ViT_L_32.yaml +23 -0
  157. mblt_vision/models/ViT_Large_Patch16_224.yaml +22 -0
  158. mblt_vision/models/ViT_Large_Patch16_384.yaml +22 -0
  159. mblt_vision/models/ViT_Large_Patch32_384.yaml +22 -0
  160. mblt_vision/models/ViT_Small_Patch16_224.yaml +22 -0
  161. mblt_vision/models/ViT_Small_Patch16_384.yaml +22 -0
  162. mblt_vision/models/ViT_Small_Patch32_224.yaml +22 -0
  163. mblt_vision/models/ViT_Small_Patch32_384.yaml +22 -0
  164. mblt_vision/models/ViT_Tiny_Patch16_224.yaml +22 -0
  165. mblt_vision/models/ViT_Tiny_Patch16_384.yaml +22 -0
  166. mblt_vision/models/VisFormer_Small.yaml +22 -0
  167. mblt_vision/models/VisFormer_Tiny.yaml +22 -0
  168. mblt_vision/models/Wide_ResNet101_2.yaml +32 -0
  169. mblt_vision/models/Wide_ResNet50_2.yaml +32 -0
  170. mblt_vision/models/YOLO11l-cls.yaml +23 -0
  171. mblt_vision/models/YOLO11l-face.yaml +27 -0
  172. mblt_vision/models/YOLO11l-obb.yaml +28 -0
  173. mblt_vision/models/YOLO11l-pose.yaml +28 -0
  174. mblt_vision/models/YOLO11l-seg.yaml +28 -0
  175. mblt_vision/models/YOLO11l.yaml +27 -0
  176. mblt_vision/models/YOLO11m-cls.yaml +23 -0
  177. mblt_vision/models/YOLO11m-face.yaml +27 -0
  178. mblt_vision/models/YOLO11m-obb.yaml +28 -0
  179. mblt_vision/models/YOLO11m-pose.yaml +28 -0
  180. mblt_vision/models/YOLO11m-seg.yaml +28 -0
  181. mblt_vision/models/YOLO11m.yaml +27 -0
  182. mblt_vision/models/YOLO11n-cls.yaml +23 -0
  183. mblt_vision/models/YOLO11n-face.yaml +27 -0
  184. mblt_vision/models/YOLO11n-obb.yaml +28 -0
  185. mblt_vision/models/YOLO11n-pose.yaml +28 -0
  186. mblt_vision/models/YOLO11n-seg.yaml +28 -0
  187. mblt_vision/models/YOLO11n.yaml +27 -0
  188. mblt_vision/models/YOLO11s-cls.yaml +23 -0
  189. mblt_vision/models/YOLO11s-face.yaml +27 -0
  190. mblt_vision/models/YOLO11s-obb.yaml +28 -0
  191. mblt_vision/models/YOLO11s-pose.yaml +28 -0
  192. mblt_vision/models/YOLO11s-seg.yaml +28 -0
  193. mblt_vision/models/YOLO11s.yaml +27 -0
  194. mblt_vision/models/YOLO11x-cls.yaml +23 -0
  195. mblt_vision/models/YOLO11x-obb.yaml +28 -0
  196. mblt_vision/models/YOLO11x-pose.yaml +28 -0
  197. mblt_vision/models/YOLO11x-seg.yaml +28 -0
  198. mblt_vision/models/YOLO11x.yaml +27 -0
  199. mblt_vision/models/YOLO12l-face.yaml +27 -0
  200. mblt_vision/models/YOLO12l-seg.yaml +28 -0
  201. mblt_vision/models/YOLO12l.yaml +27 -0
  202. mblt_vision/models/YOLO12m-face.yaml +27 -0
  203. mblt_vision/models/YOLO12m-seg.yaml +28 -0
  204. mblt_vision/models/YOLO12m.yaml +27 -0
  205. mblt_vision/models/YOLO12n-face.yaml +27 -0
  206. mblt_vision/models/YOLO12n-seg.yaml +28 -0
  207. mblt_vision/models/YOLO12n.yaml +27 -0
  208. mblt_vision/models/YOLO12s-face.yaml +27 -0
  209. mblt_vision/models/YOLO12s-seg.yaml +28 -0
  210. mblt_vision/models/YOLO12s.yaml +27 -0
  211. mblt_vision/models/YOLO12x-seg.yaml +28 -0
  212. mblt_vision/models/YOLO12x.yaml +27 -0
  213. mblt_vision/models/YOLO26l-cls.yaml +23 -0
  214. mblt_vision/models/YOLO26l-depth.yaml +17 -0
  215. mblt_vision/models/YOLO26l-distill.yaml +27 -0
  216. mblt_vision/models/YOLO26l-obb.yaml +28 -0
  217. mblt_vision/models/YOLO26l-pose.yaml +28 -0
  218. mblt_vision/models/YOLO26l-seg.yaml +28 -0
  219. mblt_vision/models/YOLO26l-sem-ade20k.yaml +17 -0
  220. mblt_vision/models/YOLO26l-sem.yaml +17 -0
  221. mblt_vision/models/YOLO26l.yaml +27 -0
  222. mblt_vision/models/YOLO26m-cls.yaml +23 -0
  223. mblt_vision/models/YOLO26m-depth.yaml +17 -0
  224. mblt_vision/models/YOLO26m-distill.yaml +27 -0
  225. mblt_vision/models/YOLO26m-obb.yaml +28 -0
  226. mblt_vision/models/YOLO26m-pose.yaml +28 -0
  227. mblt_vision/models/YOLO26m-seg.yaml +28 -0
  228. mblt_vision/models/YOLO26m-sem-ade20k.yaml +17 -0
  229. mblt_vision/models/YOLO26m-sem.yaml +17 -0
  230. mblt_vision/models/YOLO26m.yaml +27 -0
  231. mblt_vision/models/YOLO26n-cls.yaml +23 -0
  232. mblt_vision/models/YOLO26n-depth.yaml +17 -0
  233. mblt_vision/models/YOLO26n-distill.yaml +27 -0
  234. mblt_vision/models/YOLO26n-obb.yaml +28 -0
  235. mblt_vision/models/YOLO26n-pose.yaml +28 -0
  236. mblt_vision/models/YOLO26n-seg.yaml +28 -0
  237. mblt_vision/models/YOLO26n-sem-ade20k.yaml +17 -0
  238. mblt_vision/models/YOLO26n-sem.yaml +17 -0
  239. mblt_vision/models/YOLO26n.yaml +27 -0
  240. mblt_vision/models/YOLO26s-cls.yaml +23 -0
  241. mblt_vision/models/YOLO26s-depth.yaml +17 -0
  242. mblt_vision/models/YOLO26s-distill.yaml +27 -0
  243. mblt_vision/models/YOLO26s-obb.yaml +28 -0
  244. mblt_vision/models/YOLO26s-pose.yaml +28 -0
  245. mblt_vision/models/YOLO26s-seg.yaml +28 -0
  246. mblt_vision/models/YOLO26s-sem-ade20k.yaml +17 -0
  247. mblt_vision/models/YOLO26s-sem.yaml +17 -0
  248. mblt_vision/models/YOLO26s.yaml +27 -0
  249. mblt_vision/models/YOLO26x-cls.yaml +23 -0
  250. mblt_vision/models/YOLO26x-depth.yaml +17 -0
  251. mblt_vision/models/YOLO26x-distill.yaml +27 -0
  252. mblt_vision/models/YOLO26x-obb.yaml +28 -0
  253. mblt_vision/models/YOLO26x-pose.yaml +28 -0
  254. mblt_vision/models/YOLO26x-seg.yaml +28 -0
  255. mblt_vision/models/YOLO26x-sem-ade20k.yaml +17 -0
  256. mblt_vision/models/YOLO26x-sem.yaml +17 -0
  257. mblt_vision/models/YOLO26x.yaml +27 -0
  258. mblt_vision/models/YOLOv10b.yaml +28 -0
  259. mblt_vision/models/YOLOv10l-face.yaml +28 -0
  260. mblt_vision/models/YOLOv10l.yaml +28 -0
  261. mblt_vision/models/YOLOv10m-face.yaml +28 -0
  262. mblt_vision/models/YOLOv10m.yaml +28 -0
  263. mblt_vision/models/YOLOv10n-face.yaml +28 -0
  264. mblt_vision/models/YOLOv10n.yaml +28 -0
  265. mblt_vision/models/YOLOv10s-face.yaml +28 -0
  266. mblt_vision/models/YOLOv10s.yaml +28 -0
  267. mblt_vision/models/YOLOv10x.yaml +28 -0
  268. mblt_vision/models/YOLOv3-spp.yaml +44 -0
  269. mblt_vision/models/YOLOv3-sppu.yaml +27 -0
  270. mblt_vision/models/YOLOv3-tiny.yaml +38 -0
  271. mblt_vision/models/YOLOv3-tinyu.yaml +27 -0
  272. mblt_vision/models/YOLOv3.yaml +44 -0
  273. mblt_vision/models/YOLOv3u.yaml +27 -0
  274. mblt_vision/models/YOLOv5l-cls.yaml +22 -0
  275. mblt_vision/models/YOLOv5l-seg.yaml +45 -0
  276. mblt_vision/models/YOLOv5l.yaml +44 -0
  277. mblt_vision/models/YOLOv5l6.yaml +50 -0
  278. mblt_vision/models/YOLOv5l6u.yaml +27 -0
  279. mblt_vision/models/YOLOv5lu.yaml +27 -0
  280. mblt_vision/models/YOLOv5m-cls.yaml +22 -0
  281. mblt_vision/models/YOLOv5m-seg.yaml +45 -0
  282. mblt_vision/models/YOLOv5m.yaml +45 -0
  283. mblt_vision/models/YOLOv5m6.yaml +50 -0
  284. mblt_vision/models/YOLOv5m6u.yaml +27 -0
  285. mblt_vision/models/YOLOv5mu.yaml +27 -0
  286. mblt_vision/models/YOLOv5n-cls.yaml +22 -0
  287. mblt_vision/models/YOLOv5n-seg.yaml +45 -0
  288. mblt_vision/models/YOLOv5n.yaml +44 -0
  289. mblt_vision/models/YOLOv5n6.yaml +50 -0
  290. mblt_vision/models/YOLOv5n6u.yaml +27 -0
  291. mblt_vision/models/YOLOv5nu.yaml +27 -0
  292. mblt_vision/models/YOLOv5s-cls.yaml +22 -0
  293. mblt_vision/models/YOLOv5s-seg.yaml +45 -0
  294. mblt_vision/models/YOLOv5s.yaml +44 -0
  295. mblt_vision/models/YOLOv5s6.yaml +50 -0
  296. mblt_vision/models/YOLOv5s6u.yaml +27 -0
  297. mblt_vision/models/YOLOv5su.yaml +27 -0
  298. mblt_vision/models/YOLOv5x-cls.yaml +22 -0
  299. mblt_vision/models/YOLOv5x-seg.yaml +45 -0
  300. mblt_vision/models/YOLOv5x.yaml +44 -0
  301. mblt_vision/models/YOLOv5x6.yaml +50 -0
  302. mblt_vision/models/YOLOv5x6u.yaml +27 -0
  303. mblt_vision/models/YOLOv5xu.yaml +27 -0
  304. mblt_vision/models/YOLOv6m-face.yaml +27 -0
  305. mblt_vision/models/YOLOv6n-face.yaml +27 -0
  306. mblt_vision/models/YOLOv7.yaml +44 -0
  307. mblt_vision/models/YOLOv7d6.yaml +50 -0
  308. mblt_vision/models/YOLOv7e6.yaml +50 -0
  309. mblt_vision/models/YOLOv7e6e.yaml +50 -0
  310. mblt_vision/models/YOLOv7w6.yaml +50 -0
  311. mblt_vision/models/YOLOv7x.yaml +44 -0
  312. mblt_vision/models/YOLOv8l-cls.yaml +23 -0
  313. mblt_vision/models/YOLOv8l-face.yaml +27 -0
  314. mblt_vision/models/YOLOv8l-obb.yaml +28 -0
  315. mblt_vision/models/YOLOv8l-pose.yaml +28 -0
  316. mblt_vision/models/YOLOv8l-seg.yaml +28 -0
  317. mblt_vision/models/YOLOv8l.yaml +27 -0
  318. mblt_vision/models/YOLOv8m-cls.yaml +23 -0
  319. mblt_vision/models/YOLOv8m-face.yaml +27 -0
  320. mblt_vision/models/YOLOv8m-obb.yaml +28 -0
  321. mblt_vision/models/YOLOv8m-pose.yaml +28 -0
  322. mblt_vision/models/YOLOv8m-seg.yaml +28 -0
  323. mblt_vision/models/YOLOv8m.yaml +27 -0
  324. mblt_vision/models/YOLOv8n-cls.yaml +23 -0
  325. mblt_vision/models/YOLOv8n-face.yaml +27 -0
  326. mblt_vision/models/YOLOv8n-obb.yaml +28 -0
  327. mblt_vision/models/YOLOv8n-pose.yaml +28 -0
  328. mblt_vision/models/YOLOv8n-seg.yaml +28 -0
  329. mblt_vision/models/YOLOv8n.yaml +27 -0
  330. mblt_vision/models/YOLOv8s-cls.yaml +23 -0
  331. mblt_vision/models/YOLOv8s-obb.yaml +28 -0
  332. mblt_vision/models/YOLOv8s-pose.yaml +28 -0
  333. mblt_vision/models/YOLOv8s-seg.yaml +28 -0
  334. mblt_vision/models/YOLOv8s.yaml +27 -0
  335. mblt_vision/models/YOLOv8x-cls.yaml +23 -0
  336. mblt_vision/models/YOLOv8x-obb.yaml +28 -0
  337. mblt_vision/models/YOLOv8x-pose-p6.yaml +28 -0
  338. mblt_vision/models/YOLOv8x-pose.yaml +28 -0
  339. mblt_vision/models/YOLOv8x-seg.yaml +28 -0
  340. mblt_vision/models/YOLOv8x.yaml +27 -0
  341. mblt_vision/models/YOLOv9c-seg.yaml +28 -0
  342. mblt_vision/models/YOLOv9c.yaml +27 -0
  343. mblt_vision/models/YOLOv9e-seg.yaml +28 -0
  344. mblt_vision/models/YOLOv9e.yaml +27 -0
  345. mblt_vision/models/YOLOv9m.yaml +27 -0
  346. mblt_vision/models/YOLOv9s.yaml +27 -0
  347. mblt_vision/models/YOLOv9t.yaml +27 -0
  348. mblt_vision/obb/__init__.py +39 -0
  349. mblt_vision/object_detection/__init__.py +153 -0
  350. mblt_vision/pose_estimation/__init__.py +41 -0
  351. mblt_vision/py.typed +1 -0
  352. mblt_vision/semantic_segmentation/__init__.py +29 -0
  353. mblt_vision/utils/__init__.py +0 -0
  354. mblt_vision/utils/datasets/__init__.py +85 -0
  355. mblt_vision/utils/datasets/ade20k.py +41 -0
  356. mblt_vision/utils/datasets/cityscapes.py +66 -0
  357. mblt_vision/utils/datasets/coco.py +243 -0
  358. mblt_vision/utils/datasets/dataloader.py +1016 -0
  359. mblt_vision/utils/datasets/dotav1.py +59 -0
  360. mblt_vision/utils/datasets/imagenet.py +29 -0
  361. mblt_vision/utils/datasets/organizer.py +2145 -0
  362. mblt_vision/utils/datasets/readiness.py +1063 -0
  363. mblt_vision/utils/evaluation/__init__.py +51 -0
  364. mblt_vision/utils/evaluation/_result.py +22 -0
  365. mblt_vision/utils/evaluation/eval_ade20k.py +302 -0
  366. mblt_vision/utils/evaluation/eval_cityscapes.py +32 -0
  367. mblt_vision/utils/evaluation/eval_coco.py +465 -0
  368. mblt_vision/utils/evaluation/eval_dota.py +878 -0
  369. mblt_vision/utils/evaluation/eval_imagenet.py +151 -0
  370. mblt_vision/utils/evaluation/eval_nyu_depth.py +189 -0
  371. mblt_vision/utils/evaluation/eval_widerface.py +440 -0
  372. mblt_vision/utils/letterbox.py +118 -0
  373. mblt_vision/utils/postprocess/__init__.py +9 -0
  374. mblt_vision/utils/postprocess/_letterbox.py +102 -0
  375. mblt_vision/utils/postprocess/base.py +743 -0
  376. mblt_vision/utils/postprocess/build_post.py +120 -0
  377. mblt_vision/utils/postprocess/cls_post.py +117 -0
  378. mblt_vision/utils/postprocess/common.py +1571 -0
  379. mblt_vision/utils/postprocess/depth_post.py +106 -0
  380. mblt_vision/utils/postprocess/semantic_seg_post.py +192 -0
  381. mblt_vision/utils/postprocess/yolo_anchor_post.py +469 -0
  382. mblt_vision/utils/postprocess/yolo_anchorless_post.py +952 -0
  383. mblt_vision/utils/postprocess/yolo_dflfree_post.py +983 -0
  384. mblt_vision/utils/postprocess/yolo_nmsfree_post.py +176 -0
  385. mblt_vision/utils/preprocess/__init__.py +27 -0
  386. mblt_vision/utils/preprocess/_validation.py +66 -0
  387. mblt_vision/utils/preprocess/base.py +157 -0
  388. mblt_vision/utils/preprocess/build_pre.py +48 -0
  389. mblt_vision/utils/preprocess/center_crop.py +75 -0
  390. mblt_vision/utils/preprocess/letterbox.py +123 -0
  391. mblt_vision/utils/preprocess/normalize.py +94 -0
  392. mblt_vision/utils/preprocess/order.py +74 -0
  393. mblt_vision/utils/preprocess/reader.py +94 -0
  394. mblt_vision/utils/preprocess/resize.py +202 -0
  395. mblt_vision/utils/preprocess/yolo_pre.py +7 -0
  396. mblt_vision/utils/results.py +725 -0
  397. mblt_vision/utils/types.py +15 -0
  398. mblt_vision/wrapper.py +1121 -0
  399. mblt_vision_python-0.0.0.dist-info/METADATA +152 -0
  400. mblt_vision_python-0.0.0.dist-info/RECORD +404 -0
  401. mblt_vision_python-0.0.0.dist-info/WHEEL +5 -0
  402. mblt_vision_python-0.0.0.dist-info/entry_points.txt +2 -0
  403. mblt_vision_python-0.0.0.dist-info/licenses/LICENSE +28 -0
  404. mblt_vision_python-0.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,770 @@
1
+ """Markdown summary helpers for benchmark outputs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import re
7
+ import subprocess
8
+ from collections.abc import Mapping, Sequence
9
+ from datetime import datetime
10
+ from pathlib import Path
11
+ from typing import Any
12
+
13
+ HOST_PC_INFO_FILENAME = "host_pc_info.json"
14
+
15
+ _HOST_INFO_SECTION_ORDER = ("CPU", "Motherboard", "DRAM", "NPU")
16
+
17
+ _HOST_INFO_SECTION_ALIASES = {
18
+ "cpu": "CPU",
19
+ "processor": "CPU",
20
+ "motherboard": "Motherboard",
21
+ "mainboard": "Motherboard",
22
+ "baseboard": "Motherboard",
23
+ "board": "Motherboard",
24
+ "dram": "DRAM",
25
+ "memory": "DRAM",
26
+ "ram": "DRAM",
27
+ "dimm": "DRAM",
28
+ "npu": "NPU",
29
+ "npus": "NPU",
30
+ "neural": "NPU",
31
+ "accelerator": "NPU",
32
+ }
33
+
34
+ _NPU_ARRAY_KEY_RE = re.compile(r"(?:^|\.)npus\[(\d+)\]\.(.+)$")
35
+
36
+
37
+ _PLOT_TITLES_BY_NAME = {
38
+ "rtf.png": "Real-Time Factor",
39
+ "inverse_rtf.png": "Inverse Real-Time Factor",
40
+ "sec_per_j.png": "Seconds Per Joule",
41
+ "j_per_sec.png": "Joules Per Audio Second",
42
+ "wer.png": "Word Error Rate",
43
+ "cer.png": "Character Error Rate",
44
+ "p95_latency_s.png": "P95 Latency",
45
+ "throughput_samples_per_s.png": "Throughput",
46
+ "decode_tokens_per_s.png": "Decode Tokens Per Second",
47
+ "prefill_tps.png": "Prefill Tokens Per Second",
48
+ "measure_prefill_tps.png": "Prefill Tokens Per Second",
49
+ "llm_prefill_tps.png": "Prefill Tokens Per Second",
50
+ "measure_llm_prefill_tps.png": "Prefill Tokens Per Second",
51
+ "prefill_tps_per_w.png": "Prefill TPS/W",
52
+ "measure_prefill_tps_per_w.png": "Prefill TPS/W",
53
+ "llm_prefill_tps_per_w.png": "Prefill TPS/W",
54
+ "measure_llm_prefill_tps_per_w.png": "Prefill TPS/W",
55
+ "decode_tps.png": "Decode Tokens Per Second",
56
+ "measure_decode_tps.png": "Decode Tokens Per Second",
57
+ "llm_decode_tps.png": "Decode Tokens Per Second",
58
+ "measure_llm_decode_tps.png": "Decode Tokens Per Second",
59
+ "decode_tps_per_w.png": "Decode TPS/W",
60
+ "measure_decode_tps_per_w.png": "Decode TPS/W",
61
+ "llm_decode_tps_per_w.png": "Decode TPS/W",
62
+ "measure_llm_decode_tps_per_w.png": "Decode TPS/W",
63
+ "avg_power_w.png": "Power",
64
+ "measure_avg_power_w.png": "Power",
65
+ "avg_temperature_c.png": "Temperature",
66
+ "measure_avg_temperature_c.png": "Temperature",
67
+ "avg_utilization_pct.png": "Utilization",
68
+ "measure_avg_utilization_pct.png": "Utilization",
69
+ "avg_memory_used_mb.png": "Memory Used Megabytes",
70
+ "measure_avg_memory_used_mb.png": "Memory Used Megabytes",
71
+ "total_energy_j.png": "Total Energy",
72
+ "measure_total_energy_j.png": "Total Energy",
73
+ "vision_fps.png": "Vision FPS",
74
+ "vision_encode_ms.png": "Vision Encode ms",
75
+ "vision_img_per_j.png": "Vision Images Per Joule",
76
+ }
77
+
78
+ _PLOT_NAME_ORDER = {name: idx for idx, name in enumerate(_PLOT_TITLES_BY_NAME)}
79
+
80
+
81
+ def collect_host_pc_info(
82
+ results_dir: Path | str, *, filename: str = HOST_PC_INFO_FILENAME
83
+ ) -> Path:
84
+ """Run ``mblt-tracker collect`` and save its JSON output.
85
+
86
+ The benchmark should not fail just because host information collection is unavailable. Failures are therefore
87
+ converted into a JSON payload that can still be rendered in the summary.
88
+
89
+ Args:
90
+ results_dir: Benchmark output directory.
91
+ filename: JSON filename to write under ``results_dir``.
92
+
93
+ Returns:
94
+ Path to the written JSON file.
95
+ """
96
+ out_dir = Path(results_dir)
97
+ out_dir.mkdir(parents=True, exist_ok=True)
98
+ output_path = out_dir / filename
99
+ payload: Any
100
+ try:
101
+ proc = subprocess.run(
102
+ ["mblt-tracker", "collect"],
103
+ check=False,
104
+ capture_output=True,
105
+ encoding="utf-8",
106
+ timeout=30,
107
+ )
108
+ if proc.returncode == 0:
109
+ try:
110
+ payload = json.loads(proc.stdout)
111
+ except json.JSONDecodeError:
112
+ payload = {
113
+ "status": "error",
114
+ "message": "mblt-tracker collect did not return valid JSON.",
115
+ "stdout": proc.stdout,
116
+ "stderr": proc.stderr,
117
+ "returncode": proc.returncode,
118
+ }
119
+ else:
120
+ payload = {
121
+ "status": "error",
122
+ "message": "mblt-tracker collect failed.",
123
+ "stdout": proc.stdout,
124
+ "stderr": proc.stderr,
125
+ "returncode": proc.returncode,
126
+ }
127
+ except FileNotFoundError:
128
+ payload = {"status": "error", "message": "mblt-tracker CLI was not found."}
129
+ except subprocess.TimeoutExpired as e:
130
+ payload = {
131
+ "status": "error",
132
+ "message": "mblt-tracker collect timed out.",
133
+ "stdout": e.stdout,
134
+ "stderr": e.stderr,
135
+ "timeout_s": e.timeout,
136
+ }
137
+
138
+ payload = _with_collection_metadata(payload)
139
+ with output_path.open("w", encoding="utf-8") as f:
140
+ json.dump(payload, f, ensure_ascii=False, indent=2)
141
+ print(f"Saved Host PC Info: {output_path.name}")
142
+ return output_path
143
+
144
+
145
+ def write_summary_markdown(
146
+ path: Path | str,
147
+ *,
148
+ title: str,
149
+ host_info_path: Path | str | None,
150
+ table_markdown_path: Path | str | None,
151
+ plot_paths: Sequence[Path | str],
152
+ plot_tables: Mapping[str, str] | None = None,
153
+ host_info_paths: Mapping[str, Path | str] | None = None,
154
+ ) -> None:
155
+ """Write a benchmark summary Markdown file with host info, plots, and table.
156
+
157
+ Args:
158
+ path: Summary Markdown destination.
159
+ title: Document title.
160
+ host_info_path: JSON file written by :func:`collect_host_pc_info`.
161
+ table_markdown_path: Existing Markdown table to include.
162
+ plot_paths: PNG files to embed in the summary.
163
+ plot_tables: Optional Markdown tables keyed by plot PNG filename. When provided, matching tables are rendered
164
+ directly below each plot and the bottom combined table is omitted.
165
+ host_info_paths: Optional source-labeled host info JSON files. When provided, host info is rendered per source.
166
+ """
167
+ summary_path = Path(path)
168
+ summary_path.parent.mkdir(parents=True, exist_ok=True)
169
+ host_info_lines = (
170
+ _host_infos_markdown(
171
+ {label: Path(src_path) for label, src_path in host_info_paths.items()}
172
+ )
173
+ if host_info_paths
174
+ else _host_info_markdown(Path(host_info_path) if host_info_path else None)
175
+ )
176
+ lines = [f"# {title}\n\n"]
177
+ lines.extend(_device_energy_note_markdown())
178
+ lines.extend(
179
+ _plots_markdown(
180
+ summary_path.parent, [Path(p) for p in plot_paths], plot_tables=plot_tables
181
+ )
182
+ )
183
+ if not plot_tables:
184
+ lines.extend(
185
+ _table_markdown(Path(table_markdown_path) if table_markdown_path else None)
186
+ )
187
+ lines.extend(host_info_lines)
188
+ summary_path.write_text("".join(lines), encoding="utf-8")
189
+
190
+
191
+ def _device_energy_note_markdown() -> list[str]:
192
+ """Return a reusable note describing trace-integrated energy limitations."""
193
+
194
+ return [
195
+ "## Device energy note\n\n",
196
+ "Energy and energy-efficiency metrics are computed from mblt-tracker power traces using "
197
+ "trapezoidal integration. At least two valid power samples are required, so measurements shorter "
198
+ "than the tracker sampling interval may leave energy fields empty.\n\n",
199
+ ]
200
+
201
+
202
+ def read_csv_rows(path: Path | str) -> list[dict[str, str]]:
203
+ """Read CSV rows if the file exists.
204
+
205
+ Args:
206
+ path: CSV path to read.
207
+
208
+ Returns:
209
+ CSV rows as dictionaries, or an empty list when the file does not exist.
210
+ """
211
+ csv_path = Path(path)
212
+ if not csv_path.is_file():
213
+ return []
214
+ import csv
215
+
216
+ with csv_path.open("r", encoding="utf-8", newline="") as f:
217
+ return list(csv.DictReader(f))
218
+
219
+
220
+ def markdown_table(headers: Sequence[str], rows: Sequence[Sequence[Any]]) -> str:
221
+ """Build a compact Markdown table with right-aligned metric columns.
222
+
223
+ Args:
224
+ headers: Table headers.
225
+ rows: Table row values.
226
+
227
+ Returns:
228
+ Markdown table text, or an empty string for empty rows.
229
+ """
230
+ if not rows:
231
+ return ""
232
+ lines = [
233
+ "| " + " | ".join(_escape_markdown(header) for header in headers) + " |\n",
234
+ "| " + " | ".join(["---"] + ["---:" for _ in headers[1:]]) + " |\n",
235
+ ]
236
+ for row in rows:
237
+ lines.append(
238
+ "| " + " | ".join(_format_summary_cell(value) for value in row) + " |\n"
239
+ )
240
+ return "".join(lines)
241
+
242
+
243
+ def scalar_plot_table(
244
+ rows: Sequence[Mapping[str, Any]], *, value_key: str, unit_header: str
245
+ ) -> str:
246
+ """Build a model/value table for one scalar plot.
247
+
248
+ Args:
249
+ rows: Rows containing a ``model`` key and the requested scalar key.
250
+ value_key: Key containing the scalar value.
251
+ unit_header: Header for the value column.
252
+
253
+ Returns:
254
+ Markdown table text.
255
+ """
256
+ return markdown_table(
257
+ ["Model", unit_header], [[row.get("model"), row.get(value_key)] for row in rows]
258
+ )
259
+
260
+
261
+ def token_sweep_plot_table(
262
+ models: Sequence[str],
263
+ metrics_by_model: Mapping[str, Any],
264
+ *,
265
+ value_key: str,
266
+ ) -> str:
267
+ """Build a model/token table for one token-sweep plot.
268
+
269
+ Args:
270
+ models: Model names to include.
271
+ metrics_by_model: Mapping from model name to metric object with token dictionaries.
272
+ value_key: Attribute name containing a ``dict[int, float]`` token metric.
273
+
274
+ Returns:
275
+ Markdown table text.
276
+ """
277
+ token_set: set[int] = set()
278
+ for model in models:
279
+ token_set.update(getattr(metrics_by_model[model], value_key).keys())
280
+ tokens = sorted(token_set)
281
+ if not tokens:
282
+ return ""
283
+ table_rows = []
284
+ for model in models:
285
+ values = getattr(metrics_by_model[model], value_key)
286
+ table_rows.append([model, *(values.get(token) for token in tokens)])
287
+ return markdown_table(
288
+ ["Model", *(f"{token} tokens" for token in tokens)], table_rows
289
+ )
290
+
291
+
292
+ def write_token_combined_markdown(
293
+ path: Path | str,
294
+ tps_rows: Sequence[Mapping[str, Any]],
295
+ device_rows: Sequence[Mapping[str, Any]],
296
+ ) -> None:
297
+ """Write a wide-form token sweep Markdown table shared by transformer benchmarks.
298
+
299
+ Args:
300
+ path: Markdown output path.
301
+ tps_rows: Long-form TPS rows from ``BenchmarkResult.iter_rows``.
302
+ device_rows: Per-model device metric rows.
303
+ """
304
+ if not tps_rows:
305
+ return
306
+ models = sorted({str(r["model"]) for r in tps_rows})
307
+ prefill_tokens = sorted(
308
+ {
309
+ int(r["tokens"])
310
+ for r in tps_rows
311
+ if str(r.get("phase")) == "prefill" and _is_int_like(r.get("tokens"))
312
+ }
313
+ )
314
+ decode_tokens = sorted(
315
+ {
316
+ int(r["tokens"])
317
+ for r in tps_rows
318
+ if str(r.get("phase")) == "decode" and _is_int_like(r.get("tokens"))
319
+ }
320
+ )
321
+ tps_map: dict[tuple[str, str, int], float] = {}
322
+ time_map: dict[tuple[str, str, int], float] = {}
323
+ npu_pct_map: dict[tuple[str, str, int], float] = {}
324
+ for row in tps_rows:
325
+ model = str(row["model"])
326
+ phase = str(row["phase"])
327
+ token = int(row["tokens"])
328
+ tps_val = row.get("tps")
329
+ time_ms_val = row.get("time_ms")
330
+ npu_pct_val = row.get("avg_npu_token_latency_pct")
331
+ if isinstance(tps_val, (int, float)):
332
+ tps_map[(model, phase, token)] = float(tps_val)
333
+ if isinstance(time_ms_val, (int, float)):
334
+ time_map[(model, phase, token)] = float(time_ms_val)
335
+ if isinstance(npu_pct_val, (int, float)):
336
+ npu_pct_map[(model, phase, token)] = float(npu_pct_val)
337
+
338
+ device_map = {
339
+ str(r["model"]): r for r in device_rows if isinstance(r.get("model"), str)
340
+ }
341
+ device_cols = [
342
+ "avg_power_w",
343
+ "p99_power_w",
344
+ "avg_utilization_pct",
345
+ "p99_utilization_pct",
346
+ "avg_temperature_c",
347
+ "p99_temperature_c",
348
+ "avg_memory_used_mb",
349
+ "p99_memory_used_mb",
350
+ "total_memory_mb",
351
+ "avg_memory_used_pct",
352
+ "p99_memory_used_pct",
353
+ "total_energy_j",
354
+ "prefill_tps_last",
355
+ "decode_tps_last",
356
+ "prefill_tps_per_w_last",
357
+ "decode_tps_per_w_last",
358
+ "prefill_j_per_tok_last",
359
+ "decode_j_per_tok_last",
360
+ ]
361
+
362
+ headers = ["model"]
363
+ headers.extend([f"prefill_tps_{t}" for t in prefill_tokens])
364
+ headers.extend([f"decode_tps_{t}" for t in decode_tokens])
365
+ headers.extend([f"prefill_latency_ms_{t}" for t in prefill_tokens])
366
+ headers.extend([f"decode_duration_ms_{t}" for t in decode_tokens])
367
+ headers.extend([f"prefill_npu_latency_pct_{t}" for t in prefill_tokens])
368
+ headers.extend([f"decode_npu_latency_pct_{t}" for t in decode_tokens])
369
+ headers.extend(device_cols)
370
+
371
+ rows: list[list[str]] = []
372
+ for model in models:
373
+ values: list[str] = [model]
374
+ for token in prefill_tokens:
375
+ values.append(
376
+ _format_optional_float(tps_map.get((model, "prefill", token)))
377
+ )
378
+ for token in decode_tokens:
379
+ values.append(_format_optional_float(tps_map.get((model, "decode", token))))
380
+ for token in prefill_tokens:
381
+ values.append(
382
+ _format_optional_float(time_map.get((model, "prefill", token)))
383
+ )
384
+ for token in decode_tokens:
385
+ values.append(
386
+ _format_optional_float(time_map.get((model, "decode", token)))
387
+ )
388
+ for token in prefill_tokens:
389
+ values.append(
390
+ _format_optional_float(npu_pct_map.get((model, "prefill", token)))
391
+ )
392
+ for token in decode_tokens:
393
+ values.append(
394
+ _format_optional_float(npu_pct_map.get((model, "decode", token)))
395
+ )
396
+
397
+ drow = device_map.get(model, {})
398
+ for col in device_cols:
399
+ v = drow.get(col) if isinstance(drow, Mapping) else None
400
+ values.append(_format_optional_float(v))
401
+ rows.append(values)
402
+
403
+ Path(path).write_text(markdown_table(headers, rows), encoding="utf-8")
404
+
405
+
406
+ def existing_png_paths(
407
+ results_dir: Path | str, *, prefixes: Sequence[str] | None = None
408
+ ) -> list[Path]:
409
+ """Return sorted PNG paths under a benchmark results directory.
410
+
411
+ Args:
412
+ results_dir: Directory to scan.
413
+ prefixes: Optional filename prefixes to include.
414
+
415
+ Returns:
416
+ Sorted PNG files matching the optional prefixes.
417
+ """
418
+ out_dir = Path(results_dir)
419
+ paths = sorted(
420
+ out_dir.glob("*.png"),
421
+ key=lambda path: (_plot_sort_key(path), path.name),
422
+ )
423
+ if prefixes is None:
424
+ return [path for path in paths if _plot_title(path) is not None]
425
+ prefix_tuple = tuple(prefixes)
426
+ return [path for path in paths if path.name.startswith(prefix_tuple)]
427
+
428
+
429
+ def _with_collection_metadata(payload: Any) -> dict[str, Any]:
430
+ collected_at = datetime.now().astimezone().isoformat(timespec="seconds")
431
+ if isinstance(payload, dict):
432
+ out = dict(payload)
433
+ out.setdefault("status", "ok")
434
+ out.setdefault("collected_at", collected_at)
435
+ return out
436
+ return {"status": "ok", "collected_at": collected_at, "data": payload}
437
+
438
+
439
+ def _host_info_markdown(path: Path | None) -> list[str]:
440
+ lines = ["## Host PC Info\n\n"]
441
+ if path is None or not path.is_file():
442
+ lines.append("Host PC info is not available.\n\n")
443
+ return lines
444
+ try:
445
+ with path.open("r", encoding="utf-8") as f:
446
+ payload = json.load(f)
447
+ except (OSError, json.JSONDecodeError) as e:
448
+ lines.append(f"Failed to read `{path.name}`: {e}\n\n")
449
+ return lines
450
+
451
+ sections = _host_info_sections(payload)
452
+ if not sections:
453
+ lines.append("Host PC info is empty.\n\n")
454
+ return lines
455
+ lines.append(f"Source: `{path.name}`\n\n")
456
+ for title, rows in sections:
457
+ if title == "NPU":
458
+ _append_npu_info_markdown(lines, rows)
459
+ else:
460
+ _append_host_info_table(lines, title, rows)
461
+ return lines
462
+
463
+
464
+ def _host_infos_markdown(paths_by_label: Mapping[str, Path]) -> list[str]:
465
+ """Render source-labeled host info JSON files as merged comparison tables."""
466
+
467
+ lines = ["## Host PC Info\n\n"]
468
+ if not paths_by_label:
469
+ lines.append("Host PC info is not available.\n\n")
470
+ return lines
471
+
472
+ metadata_rows: list[tuple[str, dict[str, str]]] = [("Source", {}), ("Status", {})]
473
+ sections_by_label: dict[str, dict[str, list[tuple[str, str]]]] = {}
474
+ section_order: list[str] = []
475
+ for label, path in paths_by_label.items():
476
+ metadata_rows[0][1][label] = path.as_posix()
477
+ if not path.is_file():
478
+ metadata_rows[1][1][label] = (
479
+ "missing: host_pc_info.json was not found in the input folder"
480
+ )
481
+ sections_by_label[label] = {}
482
+ continue
483
+ try:
484
+ with path.open("r", encoding="utf-8") as f:
485
+ payload = json.load(f)
486
+ except (OSError, json.JSONDecodeError) as e:
487
+ metadata_rows[1][1][label] = f"error: {e}"
488
+ sections_by_label[label] = {}
489
+ continue
490
+
491
+ sections = _host_info_sections(payload)
492
+ if not sections:
493
+ metadata_rows[1][1][label] = "empty"
494
+ sections_by_label[label] = {}
495
+ continue
496
+ metadata_rows[1][1][label] = "ok"
497
+ label_sections: dict[str, list[tuple[str, str]]] = {}
498
+ for title, rows in sections:
499
+ label_sections[title] = rows
500
+ if title not in section_order:
501
+ section_order.append(title)
502
+ sections_by_label[label] = label_sections
503
+
504
+ labels = list(paths_by_label)
505
+ _append_merged_host_info_table(lines, "Sources", labels, metadata_rows)
506
+ for section in section_order:
507
+ field_values: dict[str, dict[str, str]] = {}
508
+ for label in labels:
509
+ for field, value in sections_by_label.get(label, {}).get(section, []):
510
+ field_values.setdefault(field, {})[label] = value
511
+ rows = [(field, values) for field, values in field_values.items()]
512
+ _append_merged_host_info_table(lines, section, labels, rows)
513
+ return lines
514
+
515
+
516
+ def _append_merged_host_info_table(
517
+ lines: list[str],
518
+ title: str,
519
+ labels: Sequence[str],
520
+ rows: Sequence[tuple[str, Mapping[str, str]]],
521
+ ) -> None:
522
+ """Append a source-merged host info table."""
523
+
524
+ lines.append(f"### {title}\n\n")
525
+ lines.append(
526
+ "| Field | " + " | ".join(_escape_markdown(label) for label in labels) + " |\n"
527
+ )
528
+ lines.append("| --- | " + " | ".join("---" for _ in labels) + " |\n")
529
+ for field, values_by_label in rows:
530
+ values = [_escape_markdown(values_by_label.get(label, "")) for label in labels]
531
+ lines.append(f"| `{_escape_markdown(field)}` | " + " | ".join(values) + " |\n")
532
+ lines.append("\n")
533
+
534
+
535
+ def _plots_markdown(
536
+ base_dir: Path,
537
+ plot_paths: Sequence[Path],
538
+ *,
539
+ plot_tables: Mapping[str, str] | None = None,
540
+ ) -> list[str]:
541
+ lines = ["## Plots\n\n"]
542
+ existing = [path for path in plot_paths if path.is_file()]
543
+ if not existing:
544
+ lines.append("No plot PNG files were generated.\n\n")
545
+ return lines
546
+ tables = plot_tables or {}
547
+ for path in existing:
548
+ rel = path.relative_to(base_dir) if path.is_relative_to(base_dir) else path
549
+ title = _plot_title(path) or path.stem.replace("_", " ").title()
550
+ lines.append(f"### {title}\n\n")
551
+ lines.append(f"![{title}]({rel.as_posix()})\n\n")
552
+ table = tables.get(path.name)
553
+ if table:
554
+ lines.append(table)
555
+ if not lines[-1].endswith("\n"):
556
+ lines.append("\n")
557
+ lines.append("\n")
558
+ return lines
559
+
560
+
561
+ def _plot_title(path: Path) -> str | None:
562
+ title = _PLOT_TITLES_BY_NAME.get(path.name)
563
+ if title is not None:
564
+ return title
565
+
566
+ stem = path.stem
567
+ if stem.startswith("rtf_beams"):
568
+ return "Real-Time Factor"
569
+ if stem.startswith("wer_beams"):
570
+ return "Word Error Rate"
571
+ if stem.startswith("cer_beams"):
572
+ return "Character Error Rate"
573
+ return None
574
+
575
+
576
+ def _plot_sort_key(path: Path) -> int:
577
+ if path.name in _PLOT_NAME_ORDER:
578
+ return _PLOT_NAME_ORDER[path.name]
579
+ stem = path.stem
580
+ if stem.startswith("rtf_beams"):
581
+ return len(_PLOT_NAME_ORDER)
582
+ if stem.startswith("wer_beams"):
583
+ return len(_PLOT_NAME_ORDER) + 1
584
+ if stem.startswith("cer_beams"):
585
+ return len(_PLOT_NAME_ORDER) + 2
586
+ return len(_PLOT_NAME_ORDER) + 100
587
+
588
+
589
+ def _host_info_sections(payload: Any) -> list[tuple[str, list[tuple[str, str]]]]:
590
+ rows = _flatten_json(payload)
591
+ if not rows:
592
+ return []
593
+
594
+ grouped: dict[str, list[tuple[str, str]]] = {
595
+ section: [] for section in _HOST_INFO_SECTION_ORDER
596
+ }
597
+ general_rows: list[tuple[str, str]] = []
598
+ for key, value in rows:
599
+ section = _host_info_section_for_key(key)
600
+ if section is None:
601
+ general_rows.append((key, value))
602
+ else:
603
+ grouped[section].append((key, value))
604
+
605
+ sections: list[tuple[str, list[tuple[str, str]]]] = []
606
+ if general_rows:
607
+ sections.append(("General", general_rows))
608
+ sections.extend(
609
+ (section, grouped[section])
610
+ for section in _HOST_INFO_SECTION_ORDER
611
+ if grouped[section]
612
+ )
613
+ return sections
614
+
615
+
616
+ def _append_host_info_table(
617
+ lines: list[str],
618
+ title: str,
619
+ rows: Sequence[tuple[str, str]],
620
+ *,
621
+ heading_level: int = 3,
622
+ ) -> None:
623
+ """Append one host info section as a Markdown table."""
624
+ lines.append(f"{'#' * heading_level} {title}\n\n")
625
+ _append_field_value_table(lines, rows)
626
+
627
+
628
+ def _append_npu_info_markdown(
629
+ lines: list[str], rows: Sequence[tuple[str, str]], *, heading_level: int = 3
630
+ ) -> None:
631
+ """Append NPU host info with ``npus`` array entries grouped by index."""
632
+ common_rows: list[tuple[str, str]] = []
633
+ indexed_rows: dict[int, list[tuple[str, str]]] = {}
634
+ for key, value in rows:
635
+ npu_key = _split_npu_array_key(key)
636
+ if npu_key is None:
637
+ common_rows.append((key, value))
638
+ continue
639
+ index, field = npu_key
640
+ indexed_rows.setdefault(index, []).append((field, value))
641
+
642
+ if not indexed_rows:
643
+ _append_host_info_table(lines, "NPU", rows, heading_level=heading_level)
644
+ return
645
+
646
+ lines.append(f"{'#' * heading_level} NPU\n\n")
647
+ if common_rows:
648
+ lines.append(f"{'#' * (heading_level + 1)} General\n\n")
649
+ _append_field_value_table(lines, common_rows)
650
+ for index in sorted(indexed_rows):
651
+ lines.append(f"{'#' * (heading_level + 1)} NPU {index}\n\n")
652
+ _append_field_value_table(lines, indexed_rows[index])
653
+
654
+
655
+ def _append_field_value_table(
656
+ lines: list[str], rows: Sequence[tuple[str, str]]
657
+ ) -> None:
658
+ """Append field/value rows as a Markdown table."""
659
+ lines.append("| Field | Value |\n")
660
+ lines.append("| --- | --- |\n")
661
+ for key, value in rows:
662
+ lines.append(f"| `{_escape_markdown(key)}` | {_escape_markdown(value)} |\n")
663
+ lines.append("\n")
664
+
665
+
666
+ def _host_info_section_for_key(key: str) -> str | None:
667
+ if _split_npu_array_key(key) is not None:
668
+ return "NPU"
669
+ normalized = key.replace("_", ".").replace("-", ".").replace(" ", ".").lower()
670
+ parts = [
671
+ part
672
+ for part in normalized.replace("[", ".").replace("]", ".").split(".")
673
+ if part
674
+ ]
675
+ for part in parts:
676
+ section = _HOST_INFO_SECTION_ALIASES.get(part)
677
+ if section is not None:
678
+ return section
679
+ return None
680
+
681
+
682
+ def _split_npu_array_key(key: str) -> tuple[int, str] | None:
683
+ """Return the NPU array index and field for flattened ``npus`` keys."""
684
+ match = _NPU_ARRAY_KEY_RE.search(key)
685
+ if match is None:
686
+ return None
687
+ return int(match.group(1)), match.group(2)
688
+
689
+
690
+ def _host_info_section_title(value: str) -> str:
691
+ upper_names = {"cpu": "CPU", "dram": "DRAM", "gpu": "GPU", "npu": "NPU", "os": "OS"}
692
+ normalized = value.replace("_", " ").replace("-", " ").strip()
693
+ lowered = normalized.lower()
694
+ if lowered in upper_names:
695
+ return upper_names[lowered]
696
+ return " ".join(
697
+ upper_names.get(part.lower(), part.capitalize()) for part in normalized.split()
698
+ )
699
+
700
+
701
+ def _table_markdown(path: Path | None) -> list[str]:
702
+ lines = ["## Results Table\n\n"]
703
+ if path is None or not path.is_file():
704
+ lines.append("Results table is not available.\n")
705
+ return lines
706
+ lines.append(path.read_text(encoding="utf-8"))
707
+ if not lines[-1].endswith("\n"):
708
+ lines.append("\n")
709
+ return lines
710
+
711
+
712
+ def _flatten_json(value: Any, *, prefix: str = "") -> list[tuple[str, str]]:
713
+ if isinstance(value, Mapping):
714
+ rows: list[tuple[str, str]] = []
715
+ for key, child in value.items():
716
+ child_key = f"{prefix}.{key}" if prefix else str(key)
717
+ rows.extend(_flatten_json(child, prefix=child_key))
718
+ return rows
719
+ if isinstance(value, list):
720
+ if all(not isinstance(item, (Mapping, list)) for item in value):
721
+ return [(prefix, ", ".join(_scalar_to_text(item) for item in value))]
722
+ rows = []
723
+ for idx, child in enumerate(value):
724
+ rows.extend(_flatten_json(child, prefix=f"{prefix}[{idx}]"))
725
+ return rows
726
+ return [(prefix, _scalar_to_text(value))] if prefix else []
727
+
728
+
729
+ def _scalar_to_text(value: Any) -> str:
730
+ if value is None:
731
+ return ""
732
+ if isinstance(value, (str, int, float, bool)):
733
+ return str(value)
734
+ return json.dumps(value, ensure_ascii=False)
735
+
736
+
737
+ def _format_summary_cell(value: Any) -> str:
738
+ """Format one benchmark summary table value."""
739
+ if value is None or value == "":
740
+ return ""
741
+ if isinstance(value, (int, float)):
742
+ return f"{float(value):.6f}"
743
+ if isinstance(value, str):
744
+ try:
745
+ return f"{float(value):.6f}"
746
+ except ValueError:
747
+ return _escape_markdown(value)
748
+ return _escape_markdown(str(value))
749
+
750
+
751
+ def _format_optional_float(value: Any) -> str:
752
+ """Format a numeric value for compact benchmark tables."""
753
+ return f"{float(value):.6f}" if isinstance(value, (int, float)) else ""
754
+
755
+
756
+ def _is_int_like(value: Any) -> bool:
757
+ """Return whether a value can be losslessly parsed as an integer token count."""
758
+ if isinstance(value, int):
759
+ return True
760
+ if isinstance(value, str):
761
+ try:
762
+ int(value)
763
+ except ValueError:
764
+ return False
765
+ return True
766
+ return False
767
+
768
+
769
+ def _escape_markdown(value: str) -> str:
770
+ return value.replace("|", "\\|").replace("\n", "<br>")