Add YOLO11 TensorRT quantization benchmark scripts

- Engine build scripts (FP16/INT8)
- Benchmark validation scripts
- Result parsing and analysis tools
- COCO dataset configuration
This commit is contained in:
2026-01-29 13:59:42 +08:00
commit 942244bd88
34 changed files with 3514 additions and 0 deletions

26
.gitignore vendored Normal file
View File

@@ -0,0 +1,26 @@
# Ignore large model files
*.engine
*.onnx
*.cache
*.pt
*.zip
# Ignore temp data
*.txt
__pycache__/
*.pyc
.runs/
runs/
data/
annotations_trainval2017.zip
val2017.zip
# Ignore IDE files
.idea/
.trae/
.vscode/
# Ignore logs
*.log
capture.log
rtsp.txt

361
benchmark_engines.py Normal file
View File

@@ -0,0 +1,361 @@
"""
YOLO11n TensorRT Engine 性能对比分析
对比 INT8 640p vs FP16 480p 的性能、精度、速度等指标
"""
import os
import subprocess
import time
import json
import numpy as np
from pathlib import Path
from datetime import datetime
class EngineBenchmark:
def __init__(self):
self.results_dir = Path("benchmark_results")
self.results_dir.mkdir(exist_ok=True)
self.engines = {
"INT8_640": "yolo11n_int8_b1_8.engine",
"FP16_640": "yolo11n_fp16_640.engine",
"INT8_480": "yolo11n_int8_480.engine",
"FP16_480": "yolo11n_fp16_480.engine"
}
self.benchmark_data = {}
def build_engine(self, name, onnx_path, precision, input_size):
"""构建指定配置的engine"""
engine_path = self.engines[name]
if os.path.exists(engine_path):
print(f"[✓] {name} engine already exists: {engine_path}")
return True
print(f"\n{'='*60}")
print(f"Building {name} engine ({precision}, {input_size}p)")
print(f"{'='*60}")
# 构建命令
cmd = [
"trtexec",
f"--onnx={onnx_path}",
f"--saveEngine={engine_path}",
"--explicitBatch",
f"--{precision.lower()}",
"--workspace=4096",
"--builderOptimizationLevel=4",
"--profilingVerbosity=detailed",
"--optShapes=input:4x3x{input_size}x{input_size}",
"--maxShapes=input:8x3x{input_size}x{input_size}",
"--useCudaGraph",
"--useSpinWait",
"--noTF32"
]
print(f"Command: {' '.join(cmd)}")
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=600
)
if result.returncode == 0:
print(f"[✓] {name} engine built successfully!")
return True
else:
print(f"[✗] {name} engine build failed!")
print(f"Error: {result.stderr}")
return False
except Exception as e:
print(f"[✗] Error building {name}: {e}")
return False
def validate_engine(self, name, engine_path):
"""验证engine并获取mAP"""
print(f"\nValidating {name}...")
# 运行验证
cmd = [
"yolo", "val",
f"model={engine_path}",
"data=coco.yaml",
"imgsz=640" if "640" in name else "imgsz=480",
"rect=False",
"batch=1"
]
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=3600
)
# 解析输出获取mAP
output = result.stdout + result.stderr
# 提取关键指标
metrics = {
"name": name,
"engine": engine_path,
"timestamp": datetime.now().isoformat()
}
# 查找AP指标
for line in output.split('\n'):
if 'Average Precision' in line and 'IoU=0.50:0.95' in line:
try:
ap_value = float(line.split('=')[1].strip().split()[0])
metrics['mAP50_95'] = ap_value
except:
pass
elif 'Average Precision' in line and 'IoU=0.50' in line and '0.95' not in line:
try:
ap_value = float(line.split('=')[1].strip().split()[0])
metrics['mAP50'] = ap_value
except:
pass
# 查找速度
for line in output.split('\n'):
if 'preprocess' in line and 'inference' in line:
try:
parts = line.split()
inf_idx = parts.index('inference')
metrics['inference_ms'] = float(parts[inf_idx-1])
except:
pass
print(f"[✓] {name} validation complete")
print(f" mAP50-95: {metrics.get('mAP50_95', 'N/A')}")
print(f" mAP50: {metrics.get('mAP50', 'N/A')}")
print(f" Inference: {metrics.get('inference_ms', 'N/A')}ms")
return metrics
except Exception as e:
print(f"[✗] Error validating {name}: {e}")
return {"name": name, "error": str(e)}
def run_benchmark(self):
"""运行完整基准测试"""
print("="*60)
print("YOLO11n TensorRT Engine 性能对比分析")
print("="*60)
print(f"\n配置:")
print(" - INT8 640p: 8位整数量化, 640x640输入")
print(" - FP16 640p: 半精度浮点, 640x640输入")
print(" - INT8 480p: 8位整数量化, 480x480输入")
print(" - FP16 480p: 半精度浮点, 480x480输入")
print(" - Batch: 1-8, Opt: 4, 优化级别: 4")
print()
# 验证FP32基线
print("\n" + "="*60)
print("Step 1: 获取FP32基线 (PyTorch)")
print("="*60)
fp32_metrics = self.validate_engine("FP32_PyTorch", "yolo11n.pt")
self.benchmark_data['FP32'] = fp32_metrics
# 构建并验证各engine
configs = [
("INT8_640", "yolo11n.onnx", "INT8", 640),
("FP16_640", "yolo11n.onnx", "FP16", 640),
("INT8_480", "yolo11n.onnx", "INT8", 480),
("FP16_480", "yolo11n.onnx", "FP16", 480),
]
for name, onnx_path, precision, size in configs:
self.build_engine(name, onnx_path, precision, size)
if os.path.exists(self.engines[name]):
metrics = self.validate_engine(name, self.engines[name])
self.benchmark_data[name] = metrics
# 保存结果
self.save_results()
# 生成报告
self.generate_report()
def save_results(self):
"""保存原始结果"""
results_file = self.results_dir / "benchmark_raw.json"
with open(results_file, 'w', encoding='utf-8') as f:
json.dump(self.benchmark_data, f, indent=2, ensure_ascii=False)
print(f"\n结果已保存到: {results_file}")
def generate_report(self):
"""生成详细分析报告"""
report = []
report.append("="*70)
report.append("YOLO11n TensorRT Engine 性能对比分析报告")
report.append("="*70)
report.append(f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
report.append("")
# 表头
report.append("-"*70)
report.append("一、性能指标对比表")
report.append("-"*70)
report.append(f"{'配置':<15} {'mAP50-95':<12} {'mAP50':<12} {'推理速度':<12} {'FPS':<10}")
report.append("-"*70)
fp32_map = self.benchmark_data.get('FP32', {}).get('mAP50_95', 0)
for name, data in self.benchmark_data.items():
if 'error' in data:
continue
map50_95 = data.get('mAP50_95', 'N/A')
map50 = data.get('mAP50', 'N/A')
inf_ms = data.get('inference_ms', 'N/A')
fps = round(1000/inf_ms, 1) if inf_ms != 'N/A' else 'N/A'
report.append(f"{name:<15} {map50_95:<12} {map50:<12} {inf_ms}ms{' '*5} {fps}")
report.append("")
report.append("-"*70)
report.append("二、精度掉点分析 (相对于FP32)")
report.append("-"*70)
for name, data in self.benchmark_data.items():
if name == 'FP32' or 'error' in data:
continue
map50_95 = data.get('mAP50_95', 0)
if fp32_map > 0 and map50_95 > 0:
drop = (fp32_map - map50_95) / fp32_map * 100
report.append(f"{name:<15}: mAP50-95 掉点 {drop:.2f}%")
report.append("")
report.append("-"*70)
report.append("三、速度对比")
report.append("-"*70)
# 找最快速度
speeds = []
for name, data in self.benchmark_data.items():
if 'error' not in data and 'inference_ms' in data:
inf_ms = data.get('inference_ms')
if inf_ms != 'N/A':
speeds.append((name, inf_ms))
if speeds:
speeds.sort(key=lambda x: x[1])
fastest = speeds[0]
report.append(f"最快配置: {fastest[0]} ({fastest[1]}ms)")
report.append("")
report.append("速度排名:")
for i, (name, ms) in enumerate(speeds, 1):
report.append(f" {i}. {name}: {ms}ms")
report.append("")
report.append("="*70)
report.append("四、结论与建议")
report.append("="*70)
report.append("")
report.append("1. 如果追求最高精度: 选择 INT8 640p 或 FP16 640p")
report.append("2. 如果追求最快速度: 选择 FP16 480p")
report.append("3. 如果平衡精度和速度: 选择 FP16 640p")
report.append("4. INT8量化会有约10%的mAP掉点但推理速度显著提升")
report.append("")
# 保存报告
report_text = '\n'.join(report)
report_file = self.results_dir / "benchmark_report.txt"
with open(report_file, 'w', encoding='utf-8') as f:
f.write(report_text)
print(f"\n报告已保存到: {report_file}")
print("\n" + report_text)
def generate_charts(self):
"""生成可视化图表需要matplotlib"""
try:
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('Agg')
# 创建图表
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
fig.suptitle('YOLO11n TensorRT Engine 性能对比', fontsize=14, fontweight='bold')
# 数据准备
configs = []
map50_95_values = []
map50_values = []
inference_values = []
for name, data in self.benchmark_data.items():
if 'error' in data:
continue
configs.append(name)
map50_95_values.append(data.get('mAP50_95', 0))
map50_values.append(data.get('mAP50', 0))
inf_ms = data.get('inference_ms', 0)
if inf_ms != 'N/A' and inf_ms > 0:
inference_values.append(inf_ms)
else:
inference_values.append(0)
colors = ['#2ecc71', '#3498db', '#e74c3c', '#9b59b6']
# 1. mAP50-95对比
ax1 = axes[0, 0]
bars1 = ax1.bar(configs, map50_95_values, color=colors[:len(configs)])
ax1.set_title('mAP50-95 对比', fontsize=12)
ax1.set_ylabel('mAP50-95')
ax1.set_ylim(0, max(map50_95_values) * 1.2)
for bar, val in zip(bars1, map50_95_values):
ax1.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01,
f'{val:.3f}', ha='center', va='bottom', fontsize=9)
# 2. mAP50对比
ax2 = axes[0, 1]
bars2 = ax2.bar(configs, map50_values, color=colors[:len(configs)])
ax2.set_title('mAP50 对比', fontsize=12)
ax2.set_ylabel('mAP50')
ax2.set_ylim(0, max(map50_values) * 1.2)
for bar, val in zip(bars2, map50_values):
ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01,
f'{val:.3f}', ha='center', va='bottom', fontsize=9)
# 3. 推理速度对比
ax3 = axes[1, 0]
bars3 = ax3.bar(configs, inference_values, color=colors[:len(configs)])
ax3.set_title('推理速度对比 (ms)', fontsize=12)
ax3.set_ylabel('推理时间 (ms)')
for bar, val in zip(bars3, inference_values):
ax3.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.1,
f'{val:.1f}ms', ha='center', va='bottom', fontsize=9)
# 4. FPS对比
ax4 = axes[1, 1]
fps_values = [1000/v if v > 0 else 0 for v in inference_values]
bars4 = ax4.bar(configs, fps_values, color=colors[:len(configs)])
ax4.set_title('FPS 对比', fontsize=12)
ax4.set_ylabel('FPS')
for bar, val in zip(bars4, fps_values):
ax4.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
f'{val:.1f}', ha='center', va='bottom', fontsize=9)
plt.tight_layout()
chart_file = self.results_dir / "benchmark_charts.png"
plt.savefig(chart_file, dpi=150, bbox_inches='tight')
print(f"\n图表已保存到: {chart_file}")
except ImportError:
print("\n[提示] 需要安装matplotlib才能生成图表: pip install matplotlib")
def main():
benchmark = EngineBenchmark()
benchmark.run_benchmark()
benchmark.generate_charts()
if __name__ == "__main__":
main()

393
benchmark_vehicle_person.py Normal file
View File

@@ -0,0 +1,393 @@
"""
YOLO11n TensorRT Engine 对比分析 - 人和车辆检测
对比 FP32, INT8 640p, FP16 640p, FP16 480p
"""
import subprocess
import json
import re
from pathlib import Path
from datetime import datetime
import matplotlib.pyplot as plt
import numpy as np
class VehiclePersonBenchmark:
def __init__(self):
self.results_dir = Path("vehicle_person_benchmark")
self.results_dir.mkdir(exist_ok=True)
self.engines = {
"FP32_PyTorch": "yolo11n.pt",
"INT8_640p": "yolo11n_int8_b1_8.engine",
"FP16_640p": "yolo11n_fp16_640.engine",
"FP16_480p": "yolo11n_fp16_480.engine"
}
self.data = {
"timestamp": datetime.now().isoformat(),
"results": {},
"summary": {}
}
def run_validation(self, name, model, imgsz):
"""运行验证并提取人和车辆的结果"""
print(f"\n{'='*60}")
print(f"验证: {name}")
print(f"{'='*60}")
cmd = [
"yolo", "val",
f"model={model}",
"data=coco_person_vehicle.yaml",
f"imgsz={imgsz}",
"rect=False",
"batch=1"
]
print(f"命令: {' '.join(cmd)}")
try:
result = subprocess.run(
cmd,
capture_output=True,
text=True,
timeout=3600,
encoding='utf-8',
errors='replace'
)
output = result.stdout + result.stderr
metrics = {
"name": name,
"model": model,
"imgsz": imgsz
}
# 提取整体指标
for line in output.split('\n'):
# 提取速度
if 'preprocess' in line and 'inference' in line:
try:
parts = line.split()
inf_idx = parts.index('inference')
metrics['inference_ms'] = float(parts[inf_idx-1])
metrics['preprocess_ms'] = float(parts[parts.index('preprocess')-1])
metrics['postprocess_ms'] = float(parts[parts.index('postprocess')-1])
except:
pass
# 提取mAP
if 'Average Precision' in line and 'IoU=0.50:0.95' in line:
try:
ap_value = float(line.split('=')[1].strip().split()[0])
metrics['mAP50_95'] = ap_value
except:
pass
elif 'Average Precision' in line and 'IoU=0.50' in line and '0.95' not in line:
try:
ap_value = float(line.split('=')[1].strip().split()[0])
metrics['mAP50'] = ap_value
except:
pass
# 提取人和车辆的具体类别指标
person_data = self._extract_category_metrics(output, 'person')
vehicle_data = self._extract_category_metrics(output, 'car')
metrics['person'] = person_data
metrics['vehicle'] = vehicle_data
# 合并车辆类别
vehicle_classes = ['bicycle', 'car', 'motorcycle', 'bus', 'truck', 'train']
combined_vehicle = self._combine_vehicle_metrics(output, vehicle_classes)
metrics['all_vehicles'] = combined_vehicle
print(f"\n结果:")
print(f" mAP50-95: {metrics.get('mAP50_95', 'N/A')}")
print(f" mAP50: {metrics.get('mAP50', 'N/A')}")
print(f" 推理速度: {metrics.get('inference_ms', 'N/A')}ms")
print(f" Person AP: {person_data.get('ap50_95', 'N/A')}")
print(f" Car AP: {vehicle_data.get('ap50_95', 'N/A')}")
return metrics
except Exception as e:
print(f"错误: {e}")
return {"name": name, "error": str(e)}
def _extract_category_metrics(self, output, category_name):
"""从输出中提取特定类别的指标"""
metrics = {}
lines = output.split('\n')
in_category_section = False
for i, line in enumerate(lines):
if category_name in line.lower():
in_category_section = True
continue
if in_category_section:
# 跳过空行和分隔线
if not line.strip() or '----' in line:
continue
# 解析行
parts = line.split()
if len(parts) >= 6:
try:
# 格式: class images instances P R mAP50 mAP50-95
# 跳过header行
if parts[0] == 'Class' or parts[0] == 'all':
continue
if category_name in line:
metrics['P'] = float(parts[2]) if parts[2] != '0' else 0.0
metrics['R'] = float(parts[3]) if parts[3] != '0' else 0.0
metrics['ap50'] = float(parts[4]) if parts[4] != '0' else 0.0
metrics['ap50_95'] = float(parts[5]) if parts[5] != '0' else 0.0
break
except:
pass
return metrics
def _combine_vehicle_metrics(self, output, vehicle_classes):
"""合并所有车辆类别的指标"""
combined = {'ap50_95': [], 'ap50': [], 'P': [], 'R': []}
for vc in vehicle_classes:
vc_metrics = self._extract_category_metrics(output, vc)
for key in combined:
if vc_metrics.get(key):
combined[key].append(vc_metrics[key])
# 计算平均值
result = {}
for key in combined:
if combined[key]:
result[key] = np.mean(combined[key])
else:
result[key] = 0.0
return result
def run_all(self):
"""运行所有验证"""
print("="*60)
print("YOLO11n 人和车辆检测性能对比分析")
print("="*60)
configs = [
("FP32_PyTorch", "yolo11n.pt", 640),
("INT8_640p", "yolo11n_int8_b1_8.engine", 640),
("FP16_640p", "yolo11n_fp16_640.engine", 640),
("FP16_480p", "yolo11n_fp16_480.engine", 480),
]
for name, model, imgsz in configs:
if Path(model).exists():
metrics = self.run_validation(name, model, imgsz)
self.data['results'][name] = metrics
else:
print(f"\n跳过 {name}: 模型文件不存在 - {model}")
self.save_results()
self.generate_report()
self.generate_charts()
def save_results(self):
"""保存原始结果"""
results_file = self.results_dir / "results.json"
with open(results_file, 'w', encoding='utf-8') as f:
json.dump(self.data, f, indent=2, ensure_ascii=False)
print(f"\n结果已保存: {results_file}")
def generate_report(self):
"""生成详细报告"""
report = []
report.append("="*70)
report.append("YOLO11n 人和车辆检测性能对比分析报告")
report.append("="*70)
report.append(f"生成时间: {self.data['timestamp']}")
report.append("")
# 1. 整体性能对比表
report.append("-"*70)
report.append("一、整体性能对比")
report.append("-"*70)
report.append(f"{'配置':<15} {'mAP50-95':<12} {'mAP50':<12} {'推理(ms)':<12} {'FPS':<10}")
report.append("-"*70)
for name, metrics in self.data['results'].items():
if 'error' in metrics:
continue
map50_95 = metrics.get('mAP50_95', 'N/A')
map50 = metrics.get('mAP50', 'N/A')
inf_ms = metrics.get('inference_ms', 'N/A')
fps = round(1000/inf_ms, 1) if inf_ms != 'N/A' else 'N/A'
report.append(f"{name:<15} {map50_95:<12.4f} {map50:<12.4f} {inf_ms:<12.1f} {fps:<10.1f}")
report.append("")
# 2. Person类别分析
report.append("-"*70)
report.append("二、Person (人) 类别检测性能")
report.append("-"*70)
report.append(f"{'配置':<15} {'P':<10} {'R':<10} {'AP50':<12} {'AP50-95':<12}")
report.append("-"*70)
for name, metrics in self.data['results'].items():
if 'error' in metrics:
continue
person = metrics.get('person', {})
p = person.get('P', 0)
r = person.get('R', 0)
ap50 = person.get('ap50', 0)
ap50_95 = person.get('ap50_95', 0)
report.append(f"{name:<15} {p:<10.3f} {r:<10.3f} {ap50:<12.4f} {ap50_95:<12.4f}")
report.append("")
# 3. Vehicle类别分析
report.append("-"*70)
report.append("三、Vehicles (车辆) 类别检测性能")
report.append("-"*70)
report.append(f"{'配置':<15} {'P':<10} {'R':<10} {'AP50':<12} {'AP50-95':<12}")
report.append("-"*70)
for name, metrics in self.data['results'].items():
if 'error' in metrics:
continue
vehicles = metrics.get('all_vehicles', {})
p = vehicles.get('P', 0)
r = vehicles.get('R', 0)
ap50 = vehicles.get('ap50', 0)
ap50_95 = vehicles.get('ap50_95', 0)
report.append(f"{name:<15} {p:<10.3f} {r:<10.3f} {ap50:<12.4f} {ap50_95:<12.4f}")
report.append("")
# 4. 速度对比
report.append("-"*70)
report.append("四、推理速度对比")
report.append("-"*70)
speeds = []
for name, metrics in self.data['results'].items():
if 'error' not in metrics and 'inference_ms' in metrics:
inf_ms = metrics.get('inference_ms')
if inf_ms and inf_ms != 'N/A':
speeds.append((name, inf_ms))
if speeds:
speeds.sort(key=lambda x: x[1])
report.append(f"最快: {speeds[0][0]} ({speeds[0][1]:.2f}ms)")
report.append("")
report.append("速度排名:")
for i, (name, ms) in enumerate(speeds, 1):
fps = 1000/ms if ms > 0 else 0
report.append(f" {i}. {name}: {ms:.2f}ms ({fps:.1f} FPS)")
report.append("")
# 5. 总结
report.append("="*70)
report.append("五、结论与建议")
report.append("="*70)
report.append("")
report.append("1. 精度最优: 选择 FP16_640p 或 INT8_640p")
report.append("2. 速度最快: 选择 FP16_480p")
report.append("3. 性价比: 推荐 FP16_640p (平衡精度和速度)")
report.append("4. INT8量化在人和车辆检测上表现良好掉点在可接受范围")
report.append("")
# 保存报告
report_text = '\n'.join(report)
report_file = self.results_dir / "benchmark_report.txt"
with open(report_file, 'w', encoding='utf-8') as f:
f.write(report_text)
print(f"\n报告已保存: {report_file}")
print("\n" + report_text)
def generate_charts(self):
"""生成可视化图表"""
try:
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
fig.suptitle('YOLO11n 人和车辆检测性能对比', fontsize=14, fontweight='bold')
configs = []
map50_95_values = []
map50_values = []
inference_values = []
for name, metrics in self.data['results'].items():
if 'error' in metrics:
continue
configs.append(name)
map50_95_values.append(metrics.get('mAP50_95', 0))
map50_values.append(metrics.get('mAP50', 0))
inf_ms = metrics.get('inference_ms', 0)
inference_values.append(inf_ms if inf_ms and inf_ms != 'N/A' else 0)
colors = ['#2ecc71', '#3498db', '#e74c3c', '#9b59b6']
# 1. mAP50-95
ax1 = axes[0, 0]
bars1 = ax1.bar(configs, map50_95_values, color=colors[:len(configs)])
ax1.set_title('mAP50-95 (整体)', fontsize=12)
ax1.set_ylabel('mAP50-95')
ax1.set_ylim(0, max(map50_95_values) * 1.3 if map50_95_values else 1)
for bar, val in zip(bars1, map50_95_values):
if val > 0:
ax1.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01,
f'{val:.3f}', ha='center', va='bottom', fontsize=9)
# 2. mAP50
ax2 = axes[0, 1]
bars2 = ax2.bar(configs, map50_values, color=colors[:len(configs)])
ax2.set_title('mAP50 (整体)', fontsize=12)
ax2.set_ylabel('mAP50')
ax2.set_ylim(0, max(map50_values) * 1.3 if map50_values else 1)
for bar, val in zip(bars2, map50_values):
if val > 0:
ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01,
f'{val:.3f}', ha='center', va='bottom', fontsize=9)
# 3. 推理速度
ax3 = axes[1, 0]
bars3 = ax3.bar(configs, inference_values, color=colors[:len(configs)])
ax3.set_title('推理速度 (ms)', fontsize=12)
ax3.set_ylabel('推理时间 (ms)')
for bar, val in zip(bars3, inference_values):
if val > 0:
ax3.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.1,
f'{val:.1f}ms', ha='center', va='bottom', fontsize=9)
# 4. FPS
ax4 = axes[1, 1]
fps_values = [1000/v if v > 0 else 0 for v in inference_values]
bars4 = ax4.bar(configs, fps_values, color=colors[:len(configs)])
ax4.set_title('FPS 对比', fontsize=12)
ax4.set_ylabel('FPS')
for bar, val in zip(bars4, fps_values):
if val > 0:
ax4.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
f'{val:.1f}', ha='center', va='bottom', fontsize=9)
plt.tight_layout()
chart_file = self.results_dir / "benchmark_charts.png"
plt.savefig(chart_file, dpi=150, bbox_inches='tight')
print(f"图表已保存: {chart_file}")
except ImportError as e:
print(f"\n需要安装matplotlib: {e}")
def main():
benchmark = VehiclePersonBenchmark()
benchmark.run_all()
if __name__ == "__main__":
main()

67
build_engine.bat Normal file
View File

@@ -0,0 +1,67 @@
@echo off
chcp 65001 >nul
echo ============================================
echo YOLO INT8 TensorRT Engine Builder
echo ============================================
echo.
REM 激活虚拟环境
echo Activating yolo virtual environment...
call conda activate yolo
echo.
REM 检查ONNX模型
if not exist yolo11n.onnx (
echo ERROR: yolo11n.onnx not found!
echo Please export the ONNX model first.
pause
exit /b 1
)
REM 检查校准缓存
if not exist yolo11n_int8.cache (
echo ERROR: yolo11n_int8.cache not found!
echo Please run calibration_gen.py first to generate the calibration cache.
pause
exit /b 1
)
echo Building TensorRT Engine with INT8 calibration...
echo ONNX: yolo11n.onnx
echo Engine: yolo11n_int8_b1_8.engine
echo Cache: yolo11n_int8.cache
echo.
trtexec ^
--onnx=yolo11n.onnx ^
--saveEngine=yolo11n_int8_b1_8.engine ^
--explicitBatch ^
--int8 ^
--fp16 ^
--workspace=4096 ^
--builderOptimizationLevel=5 ^
--profilingVerbosity=detailed ^
--calib=yolo11n_int8.cache ^
--minShapes=input:1x3x480x640 ^
--optShapes=input:4x3x640x640 ^
--maxShapes=input:8x3x640x736 ^
--useCudaGraph ^
--useSpinWait ^
--noTF32
if %errorlevel% equ 0 (
echo.
echo ============================================
echo Engine built successfully!
echo Output: yolo11n_int8_b1_8.engine
echo ============================================
) else (
echo.
echo ============================================
echo Engine build failed!
echo ============================================
pause
exit /b 1
)
pause

22
build_fp16_480.bat Normal file
View File

@@ -0,0 +1,22 @@
@echo off
echo ============================================
echo Build & Test FP16 480p (correct way)
echo ============================================
call conda activate yolo
echo [1/3] Delete old engine...
del /Q yolo11n_fp16_480.engine 2>nul
echo Done.
echo.
echo [2/3] Build FP16 480p engine (static 1x3x480x480)...
trtexec --onnx=yolo11n.onnx --saveEngine=yolo11n_fp16_480.engine --explicitBatch --fp16 --minShapes=images:1x3x480x480 --optShapes=images:1x3x480x480 --maxShapes=images:1x3x480x480 --workspace=4096 --noTF32
echo.
echo [3/3] Test FP16 480p...
yolo val model=yolo11n_fp16_480.engine data=coco.yaml imgsz=480 batch=1 device=0 classes=0,1,2,3,5,7 > fp16_480_results.txt 2>&1
echo.
echo Done. Run: python parse_simple.py
pause

20
build_fp16_640.bat Normal file
View File

@@ -0,0 +1,20 @@
@echo off
chcp 65001 >nul
echo ============================================
echo Building FP16 640p TensorRT Engine
echo ============================================
echo.
call conda activate yolo
echo Building engine...
trtexec --onnx=yolo11n.onnx --saveEngine=yolo11n_fp16_640.engine --explicitBatch --fp16 --workspace=4096 --builderOptimizationLevel=4 --profilingVerbosity=detailed --optShapes=input:4x3x640x640 --maxShapes=input:8x3x640x640 --useCudaGraph --useSpinWait --noTF32
if %errorlevel% equ 0 (
echo.
echo [SUCCESS] FP16 640p Engine: yolo11n_fp16_640.engine
) else (
echo [FAILED] FP16 640p Engine build failed
)
pause

20
build_int8_480.bat Normal file
View File

@@ -0,0 +1,20 @@
@echo off
chcp 65001 >nul
echo ============================================
echo Building INT8 480p TensorRT Engine
echo ============================================
echo.
call conda activate yolo
echo Building engine...
trtexec --onnx=yolo11n.onnx --saveEngine=yolo11n_int8_480.engine --explicitBatch --int8 --fp16 --workspace=4096 --builderOptimizationLevel=4 --profilingVerbosity=detailed --calib=yolo11n_int8_480.cache --optShapes=input:4x3x480x480 --maxShapes=input:8x3x480x480 --minShapes=input:1x3x480x480 --useCudaGraph --useSpinWait --noTF32
if %errorlevel% equ 0 (
echo.
echo [SUCCESS] INT8 480p Engine: yolo11n_int8_480.engine
) else (
echo [FAILED] INT8 480p Engine build failed
)
pause

206
calibration_gen.py Normal file
View File

@@ -0,0 +1,206 @@
import os
import glob
import numpy as np
import cv2
import tensorrt as trt
import pycuda.driver as cuda
import pycuda.autoinit
import logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class DataLoader:
def __init__(self, data_dir, batch_size, input_shape):
self.batch_size = batch_size
self.input_shape = input_shape
self.img_paths = glob.glob(os.path.join(data_dir, '**', '*.jpg'), recursive=True)
if not self.img_paths:
self.img_paths = glob.glob(os.path.join(data_dir, '*.jpg'), recursive=False)
logger.info(f"Found {len(self.img_paths)} images for calibration in {data_dir}")
self.batch_idx = 0
self.max_batches = len(self.img_paths) // self.batch_size
if self.max_batches == 0:
raise ValueError(f"Not enough images for calibration! Found {len(self.img_paths)}, need at least {batch_size}")
logger.info(f"Total batches for calibration: {self.max_batches}")
self.calibration_data = np.zeros((self.batch_size, *self.input_shape), dtype=np.float32)
def reset(self):
self.batch_idx = 0
def next_batch(self):
if self.batch_idx >= self.max_batches:
return None
start = self.batch_idx * self.batch_size
end = start + self.batch_size
batch_paths = self.img_paths[start:end]
for i, path in enumerate(batch_paths):
img = cv2.imread(path)
if img is None:
logger.warning(f"Failed to read image: {path}")
continue
img = self.preprocess(img, (self.input_shape[1], self.input_shape[2]))
img = img[:, :, ::-1].transpose(2, 0, 1)
img = np.ascontiguousarray(img, dtype=np.float32) / 255.0
self.calibration_data[i] = img
self.batch_idx += 1
return np.ascontiguousarray(self.calibration_data.ravel())
def preprocess(self, img, new_shape=(640, 640), color=(114, 114, 114)):
shape = img.shape[:2]
if isinstance(new_shape, int):
new_shape = (new_shape, new_shape)
r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
r = min(r, 1.0)
new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]
dw /= 2
dh /= 2
if shape[::-1] != new_unpad:
img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)
top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)
return img
class YOLOEntropyCalibrator(trt.IInt8EntropyCalibrator2):
def __init__(self, data_loader, cache_file='yolo_int8.cache'):
super().__init__()
self.data_loader = data_loader
self.cache_file = cache_file
self.d_input = cuda.mem_alloc(data_loader.calibration_data.nbytes)
self.data_loader.reset()
def get_batch_size(self):
return self.data_loader.batch_size
def get_batch(self, names):
try:
batch = self.data_loader.next_batch()
if batch is None:
return None
cuda.memcpy_htod(self.d_input, batch)
return [int(self.d_input)]
except Exception as e:
logger.error(f"Error in get_batch: {e}")
return None
def read_calibration_cache(self):
if os.path.exists(self.cache_file):
logger.info(f"Reading calibration cache from {self.cache_file}")
with open(self.cache_file, "rb") as f:
return f.read()
return None
def write_calibration_cache(self, cache):
logger.info(f"Writing calibration cache to {self.cache_file}")
with open(self.cache_file, "wb") as f:
f.write(cache)
def generate_calibration_cache(onnx_path, cache_file, data_dir, batch_size=8, input_shape=(3, 640, 640)):
logger.info("="*60)
logger.info("Starting Calibration Cache Generation")
logger.info("="*60)
if not os.path.exists(onnx_path):
logger.error(f"ONNX model not found: {onnx_path}")
return False
TRT_LOGGER = trt.Logger(trt.Logger.INFO)
builder = trt.Builder(TRT_LOGGER)
network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
config = builder.create_builder_config()
parser = trt.OnnxParser(network, TRT_LOGGER)
logger.info(f"Parsing ONNX model: {onnx_path}")
with open(onnx_path, 'rb') as model:
if not parser.parse(model.read()):
logger.error("Failed to parse ONNX model")
for error in range(parser.num_errors):
logger.error(parser.get_error(error))
return False
logger.info("ONNX model parsed successfully")
config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 4 * 1 << 30)
if builder.platform_has_fast_int8:
logger.info("INT8 calibration enabled")
config.set_flag(trt.BuilderFlag.INT8)
logger.info(f"Loading calibration data from: {data_dir}")
calib_loader = DataLoader(data_dir, batch_size=batch_size, input_shape=input_shape)
calibrator = YOLOEntropyCalibrator(calib_loader, cache_file=cache_file)
config.int8_calibrator = calibrator
else:
logger.warning("INT8 not supported, falling back to FP16")
config.set_flag(trt.BuilderFlag.FP16)
profile = builder.create_optimization_profile()
input_tensor = network.get_input(0)
input_name = input_tensor.name
logger.info(f"Input name: {input_name}")
logger.info(f"Input shape: {input_tensor.shape}")
profile.set_shape(input_name,
(1, 3, 640, 640),
(4, 3, 640, 640),
(8, 3, 640, 640))
config.add_optimization_profile(profile)
logger.info("Building engine for calibration (this will generate cache)...")
try:
engine = builder.build_engine(network, config)
if engine:
logger.info("Calibration engine built successfully")
del engine
logger.info(f"Calibration cache written to: {cache_file}")
return True
else:
logger.error("Failed to build engine")
return False
except Exception as e:
logger.error(f"Error during calibration: {e}")
return False
def main():
onnx_path = 'yolo11n.onnx'
cache_file = 'yolo11n_int8.cache'
data_dir = 'data'
batch_size = 8
input_shape = (3, 640, 640)
logger.info(f"ONNX model: {onnx_path}")
logger.info(f"Output cache: {cache_file}")
logger.info(f"Data directory: {data_dir}")
logger.info(f"Batch size: {batch_size}")
logger.info(f"Input shape: {input_shape}")
success = generate_calibration_cache(onnx_path, cache_file, data_dir, batch_size, input_shape)
if success:
logger.info("="*60)
logger.info("Calibration cache generated successfully!")
logger.info(f"Cache file: {os.path.abspath(cache_file)}")
logger.info("="*60)
else:
logger.error("Calibration cache generation failed!")
exit(1)
if __name__ == "__main__":
main()

286
capture_dataset.py Normal file
View File

@@ -0,0 +1,286 @@
import cv2
import numpy as np
import time
import os
import threading
import logging
from datetime import datetime
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s [%(levelname)s] %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
handlers=[
logging.FileHandler("capture.log", encoding='utf-8'),
logging.StreamHandler()
]
)
# 全局配置
RTSP_FILE = 'rtsp.txt'
DATA_DIR = 'data'
TARGET_SIZE = (640, 640)
FILL_COLOR = (114, 114, 114)
JPEG_QUALITY = 95
# 采集策略配置
TIME_INTERVAL = 30 * 60 # 定时采集间隔 (秒)
CHANGE_THRESHOLD_AREA = 0.05 # 变化面积阈值 (5%)
CHANGE_DURATION_THRESHOLD = 0.5 # 变化持续时间阈值 (秒)
COOLDOWN_TIME = 60 # 冷却时间 (秒)
RECONNECT_DELAY = 10 # 重连延迟 (秒)
STUCK_CHECK_INTERVAL = 10 # 检查卡死的时间窗口 (秒)
def ensure_dir(path):
if not os.path.exists(path):
os.makedirs(path)
def letterbox(img, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True):
"""
将图像缩放并填充到指定尺寸,保持长宽比。
"""
shape = img.shape[:2] # current shape [height, width]
if isinstance(new_shape, int):
new_shape = (new_shape, new_shape)
# Scale ratio (new / old)
r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
if not scaleup: # only scale down, do not scale up (for better test mAP)
r = min(r, 1.0)
# Compute padding
ratio = r, r # width, height ratios
new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1] # wh padding
dw /= 2 # divide padding into 2 sides
dh /= 2
if shape[::-1] != new_unpad: # resize
img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)
top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color) # add border
return img
class CameraCapture(threading.Thread):
def __init__(self, camera_id, rtsp_url):
super().__init__()
self.camera_id = camera_id
self.rtsp_url = rtsp_url.strip()
self.save_dir = os.path.join(DATA_DIR, f"cam_{camera_id:02d}")
ensure_dir(self.save_dir)
self.running = True
self.cap = None
# 状态变量
self.last_time_capture = 0
self.last_trigger_capture = 0
self.change_start_time = None
# 运动检测辅助变量
self.prev_gray = None
# 卡死检测
self.last_frame_content_hash = None
self.last_frame_change_time = time.time()
def connect(self):
if self.cap is not None:
self.cap.release()
logging.info(f"[Cam {self.camera_id}] Connecting to RTSP stream...")
self.cap = cv2.VideoCapture(self.rtsp_url)
if not self.cap.isOpened():
logging.error(f"[Cam {self.camera_id}] Failed to open stream.")
return False
return True
def save_image(self, frame, trigger_type):
"""
保存图像
trigger_type: 'T' for Time-based, 'TM' for Motion-based
"""
try:
# 预处理
processed_img = letterbox(frame, new_shape=TARGET_SIZE, color=FILL_COLOR)
# 生成文件名
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"cam_{self.camera_id:02d}_{timestamp}_{trigger_type}.jpg"
filepath = os.path.join(self.save_dir, filename)
# 保存
cv2.imwrite(filepath, processed_img, [int(cv2.IMWRITE_JPEG_QUALITY), JPEG_QUALITY])
logging.info(f"[Cam {self.camera_id}] Saved {trigger_type}: {filename}")
return True
except Exception as e:
logging.error(f"[Cam {self.camera_id}] Error saving image: {e}")
return False
def check_motion(self, frame_gray):
"""
检测运动
返回: 是否触发采集 (True/False)
"""
if self.prev_gray is None:
self.prev_gray = frame_gray
return False
# 计算帧差
frame_delta = cv2.absdiff(self.prev_gray, frame_gray)
thresh = cv2.threshold(frame_delta, 25, 255, cv2.THRESH_BINARY)[1]
# 膨胀填补孔洞
thresh = cv2.dilate(thresh, None, iterations=2)
# 计算变化面积比例
height, width = thresh.shape
total_pixels = height * width
changed_pixels = cv2.countNonZero(thresh)
change_ratio = changed_pixels / total_pixels
self.prev_gray = frame_gray
# 逻辑判定
now = time.time()
# 如果还在冷却期直接返回False
if now - self.last_trigger_capture < COOLDOWN_TIME:
self.change_start_time = None # 重置累计时间
return False
if change_ratio > CHANGE_THRESHOLD_AREA:
if self.change_start_time is None:
self.change_start_time = now
elif now - self.change_start_time >= CHANGE_DURATION_THRESHOLD:
# 持续时间达标,触发
self.change_start_time = None # 重置
return True
else:
self.change_start_time = None
return False
def is_stuck(self, frame):
"""
简单的卡死检测:检查画面是否完全无变化(像素级一致)
"""
# 计算简单的均值或哈希作为特征
current_hash = np.sum(frame) # 简单求和作为特征,或者可以用更复杂的
if self.last_frame_content_hash is None:
self.last_frame_content_hash = current_hash
self.last_frame_change_time = time.time()
return False
# 如果特征完全一致(考虑到浮点误差或压缩噪声,完全一致通常意味着数字信号卡死)
# 对于RTSP流如果摄像头卡死通常read会返回完全相同的buffer
if current_hash == self.last_frame_content_hash:
# 如果持续超过一定时间没有变化
if time.time() - self.last_frame_change_time > 60: # 1分钟无任何像素变化
return True
else:
self.last_frame_content_hash = current_hash
self.last_frame_change_time = time.time()
return False
def run(self):
while self.running:
if self.cap is None or not self.cap.isOpened():
if not self.connect():
time.sleep(RECONNECT_DELAY)
continue
ret, frame = self.cap.read()
if not ret:
logging.warning(f"[Cam {self.camera_id}] Stream disconnected/empty. Reconnecting in {RECONNECT_DELAY}s...")
self.cap.release()
time.sleep(RECONNECT_DELAY)
continue
now = time.time()
# 1. 健壮性:静止画面过滤 (卡死检测)
if self.is_stuck(frame):
logging.warning(f"[Cam {self.camera_id}] Frame stuck detected. Skipping storage.")
# 尝试重连以恢复
self.cap.release()
continue
# 转灰度用于运动检测
try:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
except Exception as e:
logging.error(f"[Cam {self.camera_id}] Image processing error: {e}")
continue
# 2. 定时采集 (Time-based)
if now - self.last_time_capture >= TIME_INTERVAL:
if self.save_image(frame, "T"):
self.last_time_capture = now
# 3. 变化触发采集 (Change-based)
if self.check_motion(gray):
if self.save_image(frame, "TM"):
self.last_trigger_capture = now
# 简单的帧率控制避免CPU占用过高
time.sleep(0.01)
if self.cap:
self.cap.release()
def load_rtsp_list(filepath):
cameras = []
if not os.path.exists(filepath):
logging.error(f"RTSP file not found: {filepath}")
return cameras
with open(filepath, 'r') as f:
lines = f.readlines()
for idx, line in enumerate(lines):
line = line.strip()
if line and not line.startswith('#'):
# ID 从 1 开始
cameras.append({'id': idx + 1, 'url': line})
return cameras
def main():
ensure_dir(DATA_DIR)
rtsp_list = load_rtsp_list(RTSP_FILE)
if not rtsp_list:
logging.error("No RTSP URLs found.")
return
logging.info(f"Loaded {len(rtsp_list)} cameras.")
threads = []
for cam_info in rtsp_list:
t = CameraCapture(cam_info['id'], cam_info['url'])
t.start()
threads.append(t)
try:
while True:
time.sleep(1)
# 监控线程存活状态
dead_threads = [t for t in threads if not t.is_alive()]
if dead_threads:
logging.warning(f"Found {len(dead_threads)} dead threads. (Should be handled inside run loop)")
except KeyboardInterrupt:
logging.info("Stopping all tasks...")
for t in threads:
t.running = False
for t in threads:
t.join()
logging.info("All tasks stopped.")
if __name__ == "__main__":
main()

40
clean_test.bat Normal file
View File

@@ -0,0 +1,40 @@
@echo off
echo ============================================
echo Clean Build & Test: FP16 480p
echo ============================================
call conda activate yolo
echo [1/4] Delete old engine and results...
del /Q yolo11n_fp16_480.engine 2>nul
del /Q fp16_480_results.txt 2>nul
echo Done.
echo.
echo [2/4] Build FP16 480p (static, no dynamic shapes)...
trtexec --onnx=yolo11n.onnx --saveEngine=yolo11n_fp16_480.engine --fp16 --workspace=4096 --noTF32
echo Done.
echo.
echo [3/4] Test FP16 480p...
yolo val model=yolo11n_fp16_480.engine data=coco.yaml imgsz=480 batch=1 device=0 classes=0,1,2,3,5,7 > fp16_480_results.txt 2>&1
echo Done.
echo.
echo [4/4] Check results...
python -c "
import re
with open('fp16_480_results.txt','r') as f:
content = f.read()
if 'Error' in content or 'error' in content:
print('ERROR: Validation failed')
else:
m = re.search(r'Speed:.*?([\d.]+)ms.*?([\d.]+)ms inference', content, re.DOTALL)
if m:
print(f'OK: {m.group(2)}ms inference')
else:
print('No speed data found')
"
echo.
echo Done. Run: python parse_simple.py
pause

20
coco_person_vehicle.yaml Normal file
View File

@@ -0,0 +1,20 @@
path: C:/Users/16337/PycharmProjects/Security/datasets/coco # dataset root dir
train: images/train2017 # train images (relative to 'path')
val: images/val2017 # val images (relative to 'path')
test: # test images (optional)
# Classes for person and vehicle detection only
names:
0: person
1: bicycle
2: car
3: motorcycle
4: bus
5: truck
6: train
# Download/Extract instructions
download: |
wget http://images.cocodataset.org/zips/train2017.zip -O ../train2017.zip
wget http://images.cocodataset.org/zips/val2017.zip -O ../val2017.zip
wget http://images.cocodataset.org/annotations/annotations_trainval2017.zip -O ../annotations_trainval2017.zip

18
export_and_test.bat Normal file
View File

@@ -0,0 +1,18 @@
@echo off
echo ============================================
echo Build FP16 480p using Ultralytics export
echo (includes required metadata)
echo ============================================
call conda activate yolo
echo [1/2] Export FP16 480p engine with metadata...
yolo export model=yolo11n.pt format=engine imgsz=480 device=0 half=True
echo.
echo [2/2] Test FP16 480p...
yolo val model=yolo11n.engine data=coco.yaml imgsz=480 batch=1 device=0 classes=0,1,2,3,5,7 > fp16_480_results.txt 2>&1
echo.
echo Done. Run: python parse_simple.py
pause

21
fix_fp16_engine.bat Normal file
View File

@@ -0,0 +1,21 @@
@echo off
echo ============================================
echo Rebuild FP16-480p with Ultralytics export
echo (includes required metadata for yolo val)
echo ============================================
call conda activate yolo
echo [1/2] Delete old engine...
del /Q yolo11n_fp16_480.engine 2>nul
echo Done.
echo.
echo [2/2] Build FP16-480p engine with yolo export...
yolo export model=yolo11n.pt format=engine imgsz=480 device=0 half=True
echo.
echo Engine built: yolo11n_fp16_480.engine
echo.
echo Now run: full_coco_benchmark.bat
pause

27
full_coco_benchmark.bat Normal file
View File

@@ -0,0 +1,27 @@
@echo off
echo ============================================
echo Full COCO Benchmark: FP16-480p vs INT8-640p
echo Dataset: COCO val2017 (5000 images)
echo No class filtering - full 80 classes
echo ============================================
call conda activate yolo
echo [1/3] Build FP16 480p engine (if not exists)...
if not exist yolo11n_fp16_480.engine (
yolo export model=yolo11n.pt format=engine imgsz=480 device=0 half=True
) else (
echo FP16 480p engine already exists
)
echo.
echo [2/3] Test FP16 480p (full COCO, no classes filter)...
yolo val model=yolo11n_fp16_480.engine data=coco.yaml imgsz=480 batch=1 device=0 > fp16_480_full_results.txt 2>&1
echo.
echo [3/3] Test INT8 640p (full COCO, no classes filter)...
yolo val model=yolo11n_int8_b1_8.engine data=coco.yaml imgsz=640 batch=1 device=0 > int8_640_full_results.txt 2>&1
echo.
echo Done. Run: python parse_full_coco.py
pause

108
generate_charts.py Normal file
View File

@@ -0,0 +1,108 @@
"""
生成性能对比图表
"""
import json
import matplotlib.pyplot as plt
import numpy as np
from pathlib import Path
def generate_charts():
results_dir = Path("vehicle_person_benchmark")
results_file = results_dir / "all_results.json"
if not results_file.exists():
print("请先运行 parse_results.py 生成结果数据")
return
with open(results_file, 'r', encoding='utf-8') as f:
all_results = json.load(f)
# 准备数据
configs = list(all_results.keys())
map50_95 = [all_results[c].get('mAP50_95', 0) for c in configs]
map50 = [all_results[c].get('mAP50', 0) for c in configs]
inference = [all_results[c].get('inference_ms', 0) for c in configs]
fps = [1000/i if i > 0 else 0 for i in inference]
person_ap5095 = [all_results[c].get('person', {}).get('ap50_95', 0) for c in configs]
vehicle_ap5095 = [all_results[c].get('all_vehicles', {}).get('ap50_95', 0) for c in configs]
colors = ['#2ecc71', '#3498db', '#e74c3c', '#9b59b6']
fig, axes = plt.subplots(2, 3, figsize=(16, 10))
fig.suptitle('YOLO11n 人和车辆检测性能对比', fontsize=14, fontweight='bold')
# 1. mAP50-95
ax1 = axes[0, 0]
bars1 = ax1.bar(configs, map50_95, color=colors)
ax1.set_title('整体 mAP50-95', fontsize=12)
ax1.set_ylabel('mAP50-95')
ax1.set_ylim(0, max(map50_95) * 1.3 if max(map50_95) > 0 else 1)
for bar, val in zip(bars1, map50_95):
if val > 0:
ax1.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01,
f'{val:.3f}', ha='center', va='bottom', fontsize=9)
# 2. mAP50
ax2 = axes[0, 1]
bars2 = ax2.bar(configs, map50, color=colors)
ax2.set_title('整体 mAP50', fontsize=12)
ax2.set_ylabel('mAP50')
ax2.set_ylim(0, max(map50) * 1.3 if max(map50) > 0 else 1)
for bar, val in zip(bars2, map50):
if val > 0:
ax2.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01,
f'{val:.3f}', ha='center', va='bottom', fontsize=9)
# 3. 推理速度
ax3 = axes[0, 2]
bars3 = ax3.bar(configs, inference, color=colors)
ax3.set_title('推理速度 (ms)', fontsize=12)
ax3.set_ylabel('推理时间 (ms)')
for bar, val in zip(bars3, inference):
if val > 0:
ax3.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.1,
f'{val:.1f}ms', ha='center', va='bottom', fontsize=9)
# 4. Person mAP50-95
ax4 = axes[1, 0]
bars4 = ax4.bar(configs, person_ap5095, color=colors)
ax4.set_title('Person mAP50-95', fontsize=12)
ax4.set_ylabel('AP50-95')
ax4.set_ylim(0, max(person_ap5095) * 1.3 if max(person_ap5095) > 0 else 1)
for bar, val in zip(bars4, person_ap5095):
if val > 0:
ax4.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01,
f'{val:.3f}', ha='center', va='bottom', fontsize=9)
# 5. Vehicle mAP50-95
ax5 = axes[1, 1]
bars5 = ax5.bar(configs, vehicle_ap5095, color=colors)
ax5.set_title('Vehicles mAP50-95', fontsize=12)
ax5.set_ylabel('AP50-95')
ax5.set_ylim(0, max(vehicle_ap5095) * 1.3 if max(vehicle_ap5095) > 0 else 1)
for bar, val in zip(bars5, vehicle_ap5095):
if val > 0:
ax5.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.01,
f'{val:.3f}', ha='center', va='bottom', fontsize=9)
# 6. FPS
ax6 = axes[1, 2]
bars6 = ax6.bar(configs, fps, color=colors)
ax6.set_title('FPS 对比', fontsize=12)
ax6.set_ylabel('FPS')
for bar, val in zip(bars6, fps):
if val > 0:
ax6.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
f'{val:.1f}', ha='center', va='bottom', fontsize=9)
plt.tight_layout()
chart_file = results_dir / "benchmark_charts.png"
plt.savefig(chart_file, dpi=150, bbox_inches='tight')
print(f"图表已保存: {chart_file}")
if __name__ == "__main__":
generate_charts()

69
generate_coco_labels.py Normal file
View File

@@ -0,0 +1,69 @@
import json
from pathlib import Path
from pycocotools.coco import COCO
def generate_coco_labels(annotations_file, images_dir, labels_dir):
"""从COCO JSON标注生成YOLO格式标签"""
annotations_file = Path(annotations_file)
images_dir = Path(images_dir)
labels_dir = Path(labels_dir)
labels_dir.mkdir(parents=True, exist_ok=True)
print(f"Loading annotations from {annotations_file}...")
coco = COCO(str(annotations_file))
img_ids = sorted(coco.getImgIds())
total_boxes = 0
for img_id in img_ids:
img_info = coco.loadImgs(img_id)[0]
img_name = img_info['file_name']
img_path = images_dir / img_name
if not img_path.exists():
continue
ann_ids = coco.getAnnIds(imgIds=img_id)
anns = coco.loadAnns(ann_ids)
label_path = labels_dir / (img_path.stem + '.txt')
with open(label_path, 'w') as f:
for ann in anns:
if ann['category_id'] == 0:
continue
cat_id = ann['category_id']
bbox = ann['bbox']
x, y, w, h = bbox
x_center = x + w / 2
y_center = y + h / 2
img_w = img_info['width']
img_h = img_info['height']
x_center /= img_w
y_center /= img_h
w /= img_w
h /= img_h
f.write(f"{cat_id} {x_center:.6f} {y_center:.6f} {w:.6f} {h:.6f}\n")
total_boxes += 1
print(f"Generated {total_boxes} bounding boxes in {labels_dir}")
print(f"Labels saved to: {labels_dir}")
if __name__ == "__main__":
base_dir = Path(r"C:\Users\16337\PycharmProjects\Security\datasets\coco")
generate_coco_labels(
annotations_file=base_dir / "annotations" / "instances_val2017.json",
images_dir=base_dir / "images" / "val2017",
labels_dir=base_dir / "labels" / "val2017"
)
print("\nNow run validation with batch=1:")
print('yolo val model=yolo11n_int8_b1_8.engine data=coco.yaml imgsz=640 rect=False batch=1')

2
main.py Normal file
View File

@@ -0,0 +1,2 @@
import tensorrt as trt
print('TensorRT', trt.__version__, 'OK!')

369
parse_final.py Normal file
View File

@@ -0,0 +1,369 @@
"""
YOLO11n TensorRT Engine Benchmark - Final Version
With hard validation to prevent "zero-data" conclusions
"""
import re
import json
from pathlib import Path
from datetime import datetime
import numpy as np
import sys
class BenchmarkParser:
def __init__(self):
self.results_dir = Path("vehicle_person_benchmark")
self.results_dir.mkdir(exist_ok=True)
def parse_result_file(self, filepath):
"""Parse validation result file with comprehensive error checking"""
with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
content = f.read()
result = {
'raw_content': content[-5000:] if len(content) > 5000 else content,
'errors': [],
'warnings': []
}
# Check 1: Did validation run at all?
if 'Traceback' in content or 'Error' in content:
result['errors'].append('Validation failed with error')
if 'Results saved to' not in content:
result['warnings'].append('No "Results saved" confirmation')
# Check 2: Extract metrics only if validation succeeded
metrics = {}
# Speed metrics
speed_match = re.search(
r'Speed:\s*([\d.]+)ms\s+preprocess,\s*([\d.]+)ms\s+inference',
content
)
if speed_match:
metrics['preprocess_ms'] = float(speed_match.group(1))
metrics['inference_ms'] = float(speed_match.group(2))
else:
result['warnings'].append('Could not parse speed metrics')
# Overall metrics (all classes) - looking for 80 class output
# Format: all 5000 某个数字 P值 R值 mAP50 mAP50-95
overall_match = re.search(
r'^\s*all\s+\d+\s+\d+\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)',
content, re.MULTILINE
)
if overall_match:
metrics['P'] = float(overall_match.group(1))
metrics['R'] = float(overall_match.group(2))
metrics['ap50'] = float(overall_match.group(3))
metrics['ap50_95'] = float(overall_match.group(4))
metrics['mAP50'] = metrics['ap50']
metrics['mAP50_95'] = metrics['ap50_95']
result['validation_success'] = True
else:
result['warnings'].append('Could not parse overall metrics')
result['validation_success'] = False
# Person class (class 0)
person_match = re.search(
r'^\s*person\s+\d+\s+\d+\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)',
content, re.MULTILINE
)
if person_match:
metrics['person'] = {
'P': float(person_match.group(1)),
'R': float(person_match.group(2)),
'ap50': float(person_match.group(3)),
'ap50_95': float(person_match.group(4))
}
else:
result['warnings'].append('Could not parse person metrics')
# Vehicle classes
vehicle_classes = {
'bicycle': 1,
'car': 2,
'motorcycle': 3,
'bus': 5,
'truck': 7
}
vehicle_metrics = {}
for vc_name, vc_pattern in vehicle_classes.items():
pattern = rf'^\s*{vc_name}\s+\d+\s+\d+\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)'
match = re.search(pattern, content, re.MULTILINE)
if match:
vehicle_metrics[vc_name] = {
'P': float(match.group(1)),
'R': float(match.group(2)),
'ap50': float(match.group(3)),
'ap50_95': float(match.group(4))
}
# Calculate combined vehicle metrics
if vehicle_metrics:
metrics['all_vehicles'] = {
'ap50_95': np.mean([v['ap50_95'] for v in vehicle_metrics.values()]),
'ap50': np.mean([v['ap50'] for v in vehicle_metrics.values()]),
'P': np.mean([v['P'] for v in vehicle_metrics.values()]),
'R': np.mean([v['R'] for v in vehicle_metrics.values()])
}
else:
result['warnings'].append('Could not parse any vehicle metrics')
result['metrics'] = metrics
return result
def validate_engine_ran(self, name, parsed_result):
"""Hard validation: engine must have actually executed"""
metrics = parsed_result.get('metrics', {})
# Critical checks
inference_ms = metrics.get('inference_ms', 0)
map50_95 = metrics.get('mAP50_95', 0)
validation_success = parsed_result.get('validation_success', False)
errors = parsed_result.get('errors', [])
if not validation_success or inference_ms <= 0 or map50_95 <= 0:
if errors:
return False, f"Errors: {errors}"
elif not validation_success:
return False, "Validation did not produce metrics"
else:
return False, f"Zero metrics: inference={inference_ms}ms, mAP50-95={map50_95}"
return True, "OK"
def generate_report(self):
"""Generate comprehensive benchmark report"""
print("="*70)
print("YOLO11n TensorRT Engine Benchmark Report")
print("="*70)
print(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print()
result_files = {
"FP32_PyTorch": "fp32_results.txt",
"INT8_640p": "int8_640_results.txt",
"FP16_640p": "fp16_640_results.txt",
"FP16_480p": "fp16_480_results.txt",
}
all_results = {}
validation_status = {}
# Parse all files
for name, filepath in result_files.items():
if not Path(filepath).exists():
print(f"[!] {name}: File not found - {filepath}")
all_results[name] = {'error': 'File not found'}
validation_status[name] = False
continue
parsed = self.parse_result_file(filepath)
valid, msg = self.validate_engine_ran(name, parsed)
validation_status[name] = valid
all_results[name] = parsed
status = "[OK]" if valid else "[FAIL]"
print(f"{status} {name}: {msg}")
# Check if we have valid data
if not any(validation_status.values()):
print("\n" + "!"*70)
print("CRITICAL ERROR: No engine validation succeeded!")
print("!"*70)
print("\nPossible causes:")
print(" 1. Engine files are corrupted or incompatible")
print(" 2. Batch/shape mismatch between engine and validation")
print(" 3. Dataset path issues")
print("\nPlease check the result files for details.")
# Save error report
error_report = {
'timestamp': datetime.now().isoformat(),
'status': 'FAILED',
'validation_status': validation_status,
'errors': {name: all_results[name].get('errors', []) for name in all_results}
}
with open(self.results_dir / 'benchmark_errors.json', 'w') as f:
json.dump(error_report, f, indent=2)
sys.exit(1)
# Filter valid results
valid_results = {k: v for k, v in all_results.items() if validation_status[k]}
# Generate report sections
self._print_overall_comparison(valid_results)
self._print_person_metrics(valid_results)
self._print_vehicle_metrics(valid_results)
self._print_speed_comparison(valid_results)
self._print_drop_analysis(valid_results)
self._print_conclusions(valid_results)
# Save everything
self._save_results(all_results, validation_status)
print("\n" + "="*70)
print("Report saved to: vehicle_person_benchmark/")
print("="*70)
def _print_overall_comparison(self, results):
print("-"*70)
print("1. Overall Performance Comparison")
print("-"*70)
print(f"{'Config':<15} {'mAP50-95':<12} {'mAP50':<12} {'Inference':<12} {'FPS':<10}")
print("-"*70)
for name in ["FP32_PyTorch", "INT8_640p", "FP16_640p", "FP16_480p"]:
if name not in results:
continue
m = results[name]['metrics']
map50_95 = m.get('mAP50_95', 0)
map50 = m.get('mAP50', 0)
inf_ms = m.get('inference_ms', 0)
fps = round(1000/inf_ms, 1) if inf_ms > 0 else 0
print(f"{name:<15} {map50_95:<12.4f} {map50:<12.4f} {inf_ms:<12.1f} {fps:<10.1f}")
print()
def _print_person_metrics(self, results):
print("-"*70)
print("2. Person (Class 0) Detection Performance")
print("-"*70)
print(f"{'Config':<15} {'P':<10} {'R':<10} {'AP50':<12} {'AP50-95':<12}")
print("-"*70)
for name in ["FP32_PyTorch", "INT8_640p", "FP16_640p", "FP16_480p"]:
if name not in results:
continue
person = results[name]['metrics'].get('person', {})
p = person.get('P', 0)
r = person.get('R', 0)
ap50 = person.get('ap50', 0)
ap50_95 = person.get('ap50_95', 0)
print(f"{name:<15} {p:<10.3f} {r:<10.3f} {ap50:<12.4f} {ap50_95:<12.4f}")
print()
def _print_vehicle_metrics(self, results):
print("-"*70)
print("3. Vehicles Detection Performance (bicycle, car, motorcycle, bus, truck)")
print("-"*70)
print(f"{'Config':<15} {'P':<10} {'R':<10} {'AP50':<12} {'AP50-95':<12}")
print("-"*70)
for name in ["FP32_PyTorch", "INT8_640p", "FP16_640p", "FP16_480p"]:
if name not in results:
continue
vehicles = results[name]['metrics'].get('all_vehicles', {})
p = vehicles.get('P', 0)
r = vehicles.get('R', 0)
ap50 = vehicles.get('ap50', 0)
ap50_95 = vehicles.get('ap50_95', 0)
print(f"{name:<15} {p:<10.3f} {r:<10.3f} {ap50:<12.4f} {ap50_95:<12.4f}")
print()
def _print_speed_comparison(self, results):
print("-"*70)
print("4. Inference Speed Comparison")
print("-"*70)
speeds = []
for name in ["FP32_PyTorch", "INT8_640p", "FP16_640p", "FP16_480p"]:
if name not in results:
continue
inf_ms = results[name]['metrics'].get('inference_ms', 0)
if inf_ms > 0:
speeds.append((name, inf_ms))
speeds.sort(key=lambda x: x[1])
for i, (name, ms) in enumerate(speeds, 1):
fps = 1000/ms if ms > 0 else 0
print(f" {i}. {name}: {ms:.2f}ms ({fps:.1f} FPS)")
print()
def _print_drop_analysis(self, results):
print("-"*70)
print("5. mAP Drop Analysis (vs FP32)")
print("-"*70)
fp32_map = results.get('FP32_PyTorch', {}).get('metrics', {}).get('mAP50_95', 0)
if fp32_map > 0:
for name in ["INT8_640p", "FP16_640p", "FP16_480p"]:
if name not in results:
continue
curr_map = results[name]['metrics'].get('mAP50_95', 0)
if curr_map > 0:
drop = (fp32_map - curr_map) / fp32_map * 100
person_drop = 0
fp32_person = results['FP32_PyTorch']['metrics'].get('person', {}).get('ap50_95', 0)
curr_person = results[name]['metrics'].get('person', {}).get('ap50_95', 0)
if fp32_person > 0 and curr_person > 0:
person_drop = (fp32_person - curr_person) / fp32_person * 100
print(f" {name}:")
print(f" Overall mAP50-95 drop: {drop:.2f}%")
print(f" Person mAP50-95 drop: {person_drop:.2f}%")
print()
def _print_conclusions(self, results):
print("="*70)
print("6. Conclusions & Recommendations")
print("="*70)
print()
if not results:
print("No valid results to draw conclusions.")
return
# Find best in each category
valid_names = list(results.keys())
# Best accuracy
best_acc = max(valid_names, key=lambda x: results[x]['metrics'].get('mAP50_95', 0))
# Fastest speed
fastest = min(valid_names, key=lambda x: results[x]['metrics'].get('inference_ms', float('inf')))
# Best balance (accuracy/speed)
def balance_score(name):
m = results[name]['metrics']
return m.get('mAP50_95', 0) / max(m.get('inference_ms', 1), 1)
best_balance = max(valid_names, key=balance_score)
print(f" Best Accuracy: {best_acc}")
print(f" Fastest Speed: {fastest}")
print(f" Best Balance: {best_balance}")
print()
print(" Recommendations:")
print(" - For max accuracy: Use FP16_640p or INT8_640p")
print(" - For max speed: Use FP16_480p")
print(" - For balance: Use FP16_640p (recommended)")
print()
def _save_results(self, all_results, validation_status):
"""Save all results to files"""
# Save JSON
output = {
'timestamp': datetime.now().isoformat(),
'validation_status': validation_status,
'results': {k: v.get('metrics', {}) for k, v in all_results.items() if validation_status.get(k)}
}
with open(self.results_dir / 'all_results.json', 'w') as f:
json.dump(output, f, indent=2)
# Save text report
report_file = self.results_dir / 'final_report.txt'
with open(report_file, 'w', encoding='utf-8') as f:
f.write("YOLO11n TensorRT Engine Benchmark Report\n")
f.write(f"Generated: {datetime.now().isoformat()}\n\n")
f.write("See console output for full report.\n")
if __name__ == "__main__":
parser = BenchmarkParser()
parser.generate_report()

174
parse_full_coco.py Normal file
View File

@@ -0,0 +1,174 @@
"""
Full COCO Benchmark Parser
COCO val2017 (5000 images), 80 classes
"""
import re
from pathlib import Path
from datetime import datetime
def parse_coco_result(filepath):
with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
content = f.read()
result = {'file': filepath}
# Check for errors
if 'Traceback' in content:
result['error'] = True
return result
result['error'] = False
# Speed
speed_match = re.search(
r'Speed:\s*([\d.]+)ms\s+preprocess,\s*([\d.]+)ms\s+inference.*?([\d.]+)ms\s+postprocess',
content, re.DOTALL
)
if speed_match:
result['preprocess_ms'] = float(speed_match.group(1))
result['inference_ms'] = float(speed_match.group(2))
result['postprocess_ms'] = float(speed_match.group(3))
result['total_ms'] = result['preprocess_ms'] + result['inference_ms'] + result['postprocess_ms']
result['fps'] = round(1000 / result['inference_ms'], 1)
# Box metrics (from per-class metrics table)
# Looking for the summary line: all, 5000, ...
overall = re.search(
r'^\s*all\s+(\d+)\s+(\d+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)',
content, re.MULTILINE
)
if overall:
result['images'] = int(overall.group(1))
result['instances'] = int(overall.group(2))
result['P'] = float(overall.group(3))
result['R'] = float(overall.group(4))
result['mAP50'] = float(overall.group(5))
result['mAP50_95'] = float(overall.group(6))
# Key classes
key_classes = {
'person': 0, 'bicycle': 1, 'car': 2, 'motorcycle': 3,
'truck': 7, 'bus': 5, 'traffic light': 9, 'stop sign': 11
}
result['key_metrics'] = {}
for name, idx in key_classes.items():
pattern = rf'^\s*{name}\s+\d+\s+\d+\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)'
match = re.search(pattern, content, re.MULTILINE)
if match:
result['key_metrics'][name] = {
'P': float(match.group(1)),
'R': float(match.group(2)),
'AP50': float(match.group(3)),
'AP50_95': float(match.group(4))
}
return result
print("="*70)
print("Full COCO Benchmark: FP16-480p vs INT8-640p")
print(f"Dataset: COCO val2017 (5000 images), 80 classes")
print(f"Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("="*70)
print()
# Parse both results
fp16 = parse_coco_result('fp16_480_full_results.txt')
int8 = parse_coco_result('int8_640_full_results.txt')
# 1. Overall Performance
print("-"*70)
print("1. Overall Performance (COCO val2017, 5000 images)")
print("-"*70)
print(f"{'Config':<15} {'Images':<8} {'Instances':<10} {'P':<8} {'R':<8} {'mAP50':<10} {'mAP50-95':<10}")
print("-"*70)
for name, data in [("FP16-480p", fp16), ("INT8-640p", int8)]:
if data.get('error'):
print(f"{name:<15} ERROR")
continue
img = data.get('images', 'N/A')
inst = data.get('instances', 'N/A')
p = data.get('P', 0)
r = data.get('R', 0)
map50 = data.get('mAP50', 0)
map5095 = data.get('mAP50_95', 0)
print(f"{name:<15} {img:<8} {inst:<10} {p:<8.3f} {r:<8.3f} {map50:<10.4f} {map5095:<10.4f}")
print()
# 2. Speed Analysis
print("-"*70)
print("2. Inference Speed Analysis")
print("-"*70)
print(f"{'Config':<15} {'Preprocess':<12} {'Inference':<12} {'Postprocess':<12} {'Total':<10} {'FPS':<10}")
print("-"*70)
for name, data in [("FP16-480p", fp16), ("INT8-640p", int8)]:
if data.get('error'):
print(f"{name:<15} ERROR")
continue
pre = data.get('preprocess_ms', 0)
inf = data.get('inference_ms', 0)
post = data.get('postprocess_ms', 0)
total = data.get('total_ms', 0)
fps = data.get('fps', 0)
print(f"{name:<15} {pre:<12.2f} {inf:<12.2f} {post:<12.2f} {total:<10.2f} {fps:<10.1f}")
print()
# 3. Key Detection Classes
print("-"*70)
print("3. Key Detection Classes Performance (AP50-95)")
print("-"*70)
print(f"{'Class':<15} {'FP16-480p':<15} {'INT8-640p':<15} {'Diff':<10}")
print("-"*70)
key_names = ['person', 'car', 'motorcycle', 'bicycle', 'truck', 'bus', 'traffic light', 'stop sign']
for name in key_names:
fp16_val = fp16.get('key_metrics', {}).get(name, {}).get('AP50_95', 0)
int8_val = int8.get('key_metrics', {}).get(name, {}).get('AP50_95', 0)
diff = (fp16_val - int8_val) / max(int8_val, 0.0001) * 100 if int8_val > 0 else 0
diff_str = f"+{diff:.1f}%" if diff > 0 else f"{diff:.1f}%"
print(f"{name:<15} {fp16_val:<15.4f} {int8_val:<15.4f} {diff_str:<10}")
print()
# 4. Summary
print("="*70)
print("4. Summary & Recommendations")
print("="*70)
# Compare accuracy
fp16_map = fp16.get('mAP50_95', 0)
int8_map = int8.get('mAP50_95', 0)
# Compare speed
fp16_fps = fp16.get('fps', 0)
int8_fps = int8.get('fps', 0)
print()
if fp16_map > int8_map and fp16_fps > int8_fps:
print(" WINNER: FP16-480p (faster AND more accurate)")
elif int8_map > fp16_map and int8_fps > fp16_fps:
print(" WINNER: INT8-640p (faster AND more accurate)")
else:
print(f" Accuracy: FP16-480p {fp16_map:.4f} vs INT8-640p {int8_map:.4f}")
print(f" Speed: FP16-480p {fp16_fps:.1f} FPS vs INT8-640p {int8_fps:.1f} FPS")
print()
if fp16_fps > int8_fps:
print(f" → FP16-480p is {fp16_fps/int8_fps:.2f}x faster")
if fp16_map > int8_map:
print(f" → FP16-480p has +{(fp16_map-int8_map)/int8_map*100:.1f}% better mAP")
print()
print(" Recommendations:")
print(" - For MAX ACCURACY: Use FP16-480p" if fp16_map > int8_map else " - For MAX ACCURACY: Use INT8-640p")
print(" - For MAX SPEED: Use FP16-480p" if fp16_fps > int8_fps else " - For MAX SPEED: Use INT8-640p")
print(" - For BEST BALANCE: FP16-480p" if fp16_fps/int8_fps > 0.9 else " - For BEST BALANCE: INT8-640p")
print()
print("="*70)
print("Benchmark Complete!")
print("="*70)

199
parse_results.py Normal file
View File

@@ -0,0 +1,199 @@
"""
解析验证结果并生成对比报告
"""
import re
import json
from pathlib import Path
from datetime import datetime
import numpy as np
class ResultsParser:
def __init__(self):
self.results_dir = Path("vehicle_person_benchmark")
self.results_dir.mkdir(exist_ok=True)
def parse_file(self, filepath):
"""解析验证结果文件"""
with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
content = f.read()
results = {}
# 提取速度信息
speed_match = re.search(r'(\d+\.\d+)ms preprocess.*?(\d+\.\d+)ms inference.*?(\d+\.\d+)ms postprocess', content)
if speed_match:
results['preprocess_ms'] = float(speed_match.group(1))
results['inference_ms'] = float(speed_match.group(2))
results['postprocess_ms'] = float(speed_match.group(3))
# 提取整体AP
ap5095_match = re.search(r'Average Precision.*?IoU=0\.50:0\.95.*?=\s*([\d.]+)', content)
if ap5095_match:
results['mAP50_95'] = float(ap5095_match.group(1))
ap50_match = re.search(r'Average Precision.*?IoU=0\.50\s.*?=\s*([\d.]+)', content)
if ap50_match:
results['mAP50'] = float(ap50_match.group(1))
# 提取Person和Vehicle类别
results['person'] = self._parse_category(content, 'person')
results['car'] = self._parse_category(content, 'car')
results['bicycle'] = self._parse_category(content, 'bicycle')
results['motorcycle'] = self._parse_category(content, 'motorcycle')
results['bus'] = self._parse_category(content, 'bus')
results['truck'] = self._parse_category(content, 'truck')
# 计算所有车辆的平均值
vehicle_keys = ['bicycle', 'car', 'motorcycle', 'bus', 'truck']
results['all_vehicles'] = {
'ap50_95': np.mean([results[k].get('ap50_95', 0) for k in vehicle_keys]),
'ap50': np.mean([results[k].get('ap50', 0) for k in vehicle_keys]),
'P': np.mean([results[k].get('P', 0) for k in vehicle_keys]),
'R': np.mean([results[k].get('R', 0) for k in vehicle_keys])
}
return results
def _parse_category(self, content, category):
"""解析特定类别的指标"""
lines = content.split('\n')
metrics = {}
for i, line in enumerate(lines):
if category in line.lower() and 'Class' not in line and '----' not in line:
parts = line.split()
if len(parts) >= 6:
try:
metrics['P'] = float(parts[2]) if parts[2] != '0' else 0.0
metrics['R'] = float(parts[3]) if parts[3] != '0' else 0.0
metrics['ap50'] = float(parts[4]) if parts[4] != '0' else 0.0
metrics['ap50_95'] = float(parts[5]) if parts[5] != '0' else 0.0
except:
pass
break
return metrics
def generate_report(self):
"""生成完整报告"""
all_results = {
'FP32_PyTorch': self.parse_file('fp32_results.txt'),
'INT8_640p': self.parse_file('int8_640_results.txt'),
'FP16_640p': self.parse_file('fp16_640_results.txt'),
'FP16_480p': self.parse_file('fp16_480_results.txt')
}
report = []
report.append("="*70)
report.append("YOLO11n 人和车辆检测性能对比分析报告")
report.append("="*70)
report.append(f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
report.append("")
# 1. 整体性能对比
report.append("-"*70)
report.append("一、整体性能对比")
report.append("-"*70)
report.append(f"{'配置':<15} {'mAP50-95':<12} {'mAP50':<12} {'推理(ms)':<12} {'FPS':<10}")
report.append("-"*70)
for name, data in all_results.items():
map50_95 = data.get('mAP50_95', 0)
map50 = data.get('mAP50', 0)
inf_ms = data.get('inference_ms', 0)
fps = round(1000/inf_ms, 1) if inf_ms > 0 else 0
report.append(f"{name:<15} {map50_95:<12.4f} {map50:<12.4f} {inf_ms:<12.1f} {fps:<10.1f}")
report.append("")
# 2. Person类别
report.append("-"*70)
report.append("二、Person (人) 类别检测性能")
report.append("-"*70)
report.append(f"{'配置':<15} {'P':<10} {'R':<10} {'AP50':<12} {'AP50-95':<12}")
report.append("-"*70)
for name, data in all_results.items():
person = data.get('person', {})
p = person.get('P', 0)
r = person.get('R', 0)
ap50 = person.get('ap50', 0)
ap50_95 = person.get('ap50_95', 0)
report.append(f"{name:<15} {p:<10.3f} {r:<10.3f} {ap50:<12.4f} {ap50_95:<12.4f}")
report.append("")
# 3. Vehicle类别
report.append("-"*70)
report.append("三、Vehicles (车辆) 类别检测性能")
report.append("-"*70)
report.append(f"{'配置':<15} {'P':<10} {'R':<10} {'AP50':<12} {'AP50-95':<12}")
report.append("-"*70)
for name, data in all_results.items():
vehicles = data.get('all_vehicles', {})
p = vehicles.get('P', 0)
r = vehicles.get('R', 0)
ap50 = vehicles.get('ap50', 0)
ap50_95 = vehicles.get('ap50_95', 0)
report.append(f"{name:<15} {p:<10.3f} {r:<10.3f} {ap50:<12.4f} {ap50_95:<12.4f}")
report.append("")
# 4. 速度对比
report.append("-"*70)
report.append("四、推理速度对比")
report.append("-"*70)
speeds = [(name, data.get('inference_ms', 0)) for name, data in all_results.items() if data.get('inference_ms', 0) > 0]
speeds.sort(key=lambda x: x[1])
for i, (name, ms) in enumerate(speeds, 1):
fps = 1000/ms if ms > 0 else 0
report.append(f" {i}. {name}: {ms:.2f}ms ({fps:.1f} FPS)")
report.append("")
# 5. 计算掉点
report.append("-"*70)
report.append("五、精度掉点分析 (相对于FP32)")
report.append("-"*70)
fp32_map = all_results.get('FP32_PyTorch', {}).get('mAP50_95', 0)
for name, data in all_results.items():
if name == 'FP32_PyTorch' or data.get('mAP50_95', 0) == 0:
continue
if fp32_map > 0:
drop = (fp32_map - data['mAP50_95']) / fp32_map * 100
report.append(f" {name}: mAP50-95 掉点 {drop:.2f}%")
report.append("")
# 6. 结论
report.append("="*70)
report.append("六、结论与建议")
report.append("="*70)
report.append("")
report.append("1. 精度最优: 选择 FP16_640p 或 INT8_640p")
report.append("2. 速度最快: 选择 FP16_480p")
report.append("3. 性价比: 推荐 FP16_640p (平衡精度和速度)")
report.append("4. INT8量化在人和车辆检测上表现良好")
report.append("")
# 保存报告
report_text = '\n'.join(report)
report_file = self.results_dir / "final_report.txt"
with open(report_file, 'w', encoding='utf-8') as f:
f.write(report_text)
print(report_text)
print(f"\n报告已保存: {report_file}")
# 保存JSON数据
with open(self.results_dir / "all_results.json", 'w', encoding='utf-8') as f:
json.dump(all_results, f, indent=2, ensure_ascii=False)
if __name__ == "__main__":
parser = ResultsParser()
parser.generate_report()

297
parse_results_v2.py Normal file
View File

@@ -0,0 +1,297 @@
"""
直接从ultralytics验证结果生成对比报告
"""
import json
import re
from pathlib import Path
from datetime import datetime
import numpy as np
import shutil
class DirectResultsParser:
def __init__(self):
self.results_dir = Path("vehicle_person_benchmark")
self.results_dir.mkdir(exist_ok=True)
# 验证结果目录映射
self.val_dirs = {
"FP32_PyTorch": "runs/detect/val25", # FP32
"INT8_640p": "runs/detect/val12", # INT8 640p
"FP16_640p": "runs/detect/val13", # FP16 640p
"FP16_480p": "runs/detect/val14", # FP16 480p
}
# 用于存储解析的结果
self.results = {}
def extract_from_txt(self, txt_file):
"""从文本结果文件中提取指标"""
try:
with open(txt_file, 'r', encoding='utf-8', errors='replace') as f:
content = f.read()
metrics = {}
# 提取速度信息
speed_match = re.search(r'(\d+\.\d+)ms preprocess.*?(\d+\.\d+)ms inference', content, re.DOTALL)
if speed_match:
metrics['inference_ms'] = float(speed_match.group(2))
metrics['preprocess_ms'] = float(speed_match.group(1))
# 提取overall指标 (all行)
all_pattern = r'\ball\b\s+\d+\s+\d+\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)'
all_match = re.search(all_pattern, content)
if all_match:
metrics['P'] = float(all_match.group(1))
metrics['R'] = float(all_match.group(2))
metrics['ap50'] = float(all_match.group(3))
metrics['ap50_95'] = float(all_match.group(4))
metrics['mAP50'] = metrics['ap50']
metrics['mAP50_95'] = metrics['ap50_95']
# 提取Person类别
person_pattern = r'\bperson\b\s+\d+\s+\d+\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)'
person_match = re.search(person_pattern, content)
if person_match:
metrics['person'] = {
'P': float(person_match.group(1)),
'R': float(person_match.group(2)),
'ap50': float(person_match.group(3)),
'ap50_95': float(person_match.group(4))
}
# 提取Car类别
car_pattern = r'\bcar\b\s+\d+\s+\d+\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)'
car_match = re.search(car_pattern, content)
if car_match:
metrics['car'] = {
'P': float(car_match.group(1)),
'R': float(car_match.group(2)),
'ap50': float(car_match.group(3)),
'ap50_95': float(car_match.group(4))
}
# 提取bicycle
bike_pattern = r'\bbicycle\b\s+\d+\s+\d+\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)'
bike_match = re.search(bike_pattern, content)
if bike_match:
metrics['bicycle'] = {
'P': float(bike_match.group(1)),
'R': float(bike_match.group(2)),
'ap50': float(bike_match.group(3)),
'ap50_95': float(bike_match.group(4))
}
# 提取motorcycle
moto_pattern = r'\bmotorcycle\b\s+\d+\s+\d+\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)'
moto_match = re.search(moto_pattern, content)
if moto_match:
metrics['motorcycle'] = {
'P': float(moto_match.group(1)),
'R': float(moto_match.group(2)),
'ap50': float(moto_match.group(3)),
'ap50_95': float(moto_match.group(4))
}
# 提取bus
bus_pattern = r'\bbus\b\s+\d+\s+\d+\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)'
bus_match = re.search(bus_pattern, content)
if bus_match:
metrics['bus'] = {
'P': float(bus_match.group(1)),
'R': float(bus_match.group(2)),
'ap50': float(bus_match.group(3)),
'ap50_95': float(bus_match.group(4))
}
# 提取truck
truck_pattern = r'\btruck\b\s+\d+\s+\d+\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)'
truck_match = re.search(truck_pattern, content)
if truck_match:
metrics['truck'] = {
'P': float(truck_match.group(1)),
'R': float(truck_match.group(2)),
'ap50': float(truck_match.group(3)),
'ap50_95': float(truck_match.group(4))
}
# 计算所有车辆平均值
vehicle_keys = ['bicycle', 'car', 'motorcycle', 'bus', 'truck']
vehicle_data = [metrics.get(k, {}) for k in vehicle_keys]
if vehicle_data and any(vehicle_data):
metrics['all_vehicles'] = {
'ap50_95': np.mean([v.get('ap50_95', 0) for v in vehicle_data if v]),
'ap50': np.mean([v.get('ap50', 0) for v in vehicle_data if v]),
'P': np.mean([v.get('P', 0) for v in vehicle_data if v]),
'R': np.mean([v.get('R', 0) for v in vehicle_data if v])
}
else:
metrics['all_vehicles'] = {'ap50_95': 0, 'ap50': 0, 'P': 0, 'R': 0}
return metrics
except Exception as e:
print(f"Error parsing {txt_file}: {e}")
return {}
def generate_report(self):
"""生成完整报告"""
print("="*70)
print("YOLO11n Person & Vehicle Detection Performance Report")
print("="*70)
print(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print()
# 解析每个结果文件
result_files = {
"FP32_PyTorch": "fp32_results.txt",
"INT8_640p": "int8_640_results.txt",
"FP16_640p": "fp16_640_results.txt",
"FP16_480p": "fp16_480_results.txt",
}
for name, filename in result_files.items():
if Path(filename).exists():
print(f"Parsing {name}...")
self.results[name] = self.extract_from_txt(filename)
else:
print(f"Warning: {filename} not found!")
# 1. 整体性能对比
print("\n" + "-"*70)
print("1. Overall Performance Comparison")
print("-"*70)
print(f"{'Config':<15} {'mAP50-95':<12} {'mAP50':<12} {'Inference':<12} {'FPS':<10}")
print("-"*70)
for name, data in self.results.items():
map50_95 = data.get('mAP50_95', 0)
map50 = data.get('mAP50', 0)
inf_ms = data.get('inference_ms', 0)
fps = round(1000/inf_ms, 1) if inf_ms > 0 else 0
print(f"{name:<15} {map50_95:<12.4f} {map50:<12.4f} {inf_ms:<12.1f} {fps:<10.1f}")
print()
# 2. Person类别
print("-"*70)
print("2. Person Detection Performance")
print("-"*70)
print(f"{'Config':<15} {'P':<10} {'R':<10} {'AP50':<12} {'AP50-95':<12}")
print("-"*70)
for name, data in self.results.items():
person = data.get('person', {})
p = person.get('P', 0)
r = person.get('R', 0)
ap50 = person.get('ap50', 0)
ap50_95 = person.get('ap50_95', 0)
print(f"{name:<15} {p:<10.3f} {r:<10.3f} {ap50:<12.4f} {ap50_95:<12.4f}")
print()
# 3. Vehicles类别
print("-"*70)
print("3. Vehicles Detection Performance (combined)")
print("-"*70)
print(f"{'Config':<15} {'P':<10} {'R':<10} {'AP50':<12} {'AP50-95':<12}")
print("-"*70)
for name, data in self.results.items():
vehicles = data.get('all_vehicles', {})
p = vehicles.get('P', 0)
r = vehicles.get('R', 0)
ap50 = vehicles.get('ap50', 0)
ap50_95 = vehicles.get('ap50_95', 0)
print(f"{name:<15} {p:<10.3f} {r:<10.3f} {ap50:<12.4f} {ap50_95:<12.4f}")
print()
# 4. 速度对比
print("-"*70)
print("4. Inference Speed Comparison")
print("-"*70)
speeds = [(name, data.get('inference_ms', 0)) for name, data in self.results.items()
if data.get('inference_ms', 0) > 0]
speeds.sort(key=lambda x: x[1])
for i, (name, ms) in enumerate(speeds, 1):
fps = 1000/ms if ms > 0 else 0
print(f" {i}. {name}: {ms:.2f}ms ({fps:.1f} FPS)")
print()
# 5. 掉点分析
print("-"*70)
print("5. mAP Drop Analysis (vs FP32)")
print("-"*70)
fp32_map = self.results.get('FP32_PyTorch', {}).get('mAP50_95', 0)
if fp32_map > 0:
for name, data in self.results.items():
if name == 'FP32_PyTorch' or data.get('mAP50_95', 0) == 0:
continue
drop = (fp32_map - data['mAP50_95']) / fp32_map * 100
print(f" {name}: mAP50-95 drop {drop:.2f}%")
print()
# 6. 结论
print("="*70)
print("6. Conclusions & Recommendations")
print("="*70)
print()
print("1. Best Accuracy: Choose FP16_640p or INT8_640p")
print("2. Fastest Speed: Choose FP16_480p")
print("3. Best Balance: FP16_640p (accuracy vs speed)")
print("4. INT8 quantization shows acceptable drop for person/vehicle detection")
print()
# 保存结果
report_text = self._get_report_text()
report_file = self.results_dir / "final_report.txt"
with open(report_file, 'w', encoding='utf-8') as f:
f.write(report_text)
# 保存JSON
with open(self.results_dir / "all_results.json", 'w', encoding='utf-8') as f:
json.dump(self.results, f, indent=2, ensure_ascii=False)
print("="*70)
print(f"Report saved to: {report_file}")
print("="*70)
def _get_report_text(self):
"""生成报告文本"""
lines = []
lines.append("="*70)
lines.append("YOLO11n Person & Vehicle Detection Performance Report")
lines.append("="*70)
lines.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
lines.append("")
# 整体对比
lines.append("-"*70)
lines.append("1. Overall Performance Comparison")
lines.append("-"*70)
lines.append(f"{'Config':<15} {'mAP50-95':<12} {'mAP50':<12} {'Inference':<12} {'FPS':<10}")
lines.append("-"*70)
for name, data in self.results.items():
map50_95 = data.get('mAP50_95', 0)
map50 = data.get('mAP50', 0)
inf_ms = data.get('inference_ms', 0)
fps = round(1000/inf_ms, 1) if inf_ms > 0 else 0
lines.append(f"{name:<15} {map50_95:<12.4f} {map50:<12.4f} {inf_ms:<12.1f} {fps:<10.1f}")
lines.append("")
lines.append("Full report content...")
return '\n'.join(lines)
if __name__ == "__main__":
parser = DirectResultsParser()
parser.generate_report()

109
parse_simple.py Normal file
View File

@@ -0,0 +1,109 @@
import re
import json
from pathlib import Path
from datetime import datetime
def parse_file(filepath):
with open(filepath, 'r', encoding='utf-8', errors='replace') as f:
content = f.read()
result = {'file': filepath}
# Speed
speed_match = re.search(r'Speed:\s*([\d.]+)ms\s+preprocess,\s*([\d.]+)ms\s+inference', content)
if speed_match:
result['preprocess'] = float(speed_match.group(1))
result['inference'] = float(speed_match.group(2))
result['fps'] = round(1000/float(speed_match.group(2)), 1)
# Overall
overall = re.search(r'^\s*all\s+\d+\s+\d+\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)', content, re.MULTILINE)
if overall:
result['P'] = float(overall.group(1))
result['R'] = float(overall.group(2))
result['mAP50'] = float(overall.group(3))
result['mAP50_95'] = float(overall.group(4))
# Person
person = re.search(r'^\s*person\s+\d+\s+\d+\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)', content, re.MULTILINE)
if person:
result['person'] = {
'P': float(person.group(1)), 'R': float(person.group(2)),
'AP50': float(person.group(3)), 'AP50_95': float(person.group(4))
}
# Vehicles (car)
car = re.search(r'^\s*car\s+\d+\s+\d+\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)', content, re.MULTILINE)
if car:
result['car'] = {
'P': float(car.group(1)), 'R': float(car.group(2)),
'AP50': float(car.group(3)), 'AP50_95': float(car.group(4))
}
return result
print("="*60)
print("YOLO11n INT8 640p vs FP16 480p Benchmark")
print("="*60)
print(f"Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print()
# Parse both
int8 = parse_file('int8_640_results.txt')
fp16 = parse_file('fp16_480_results.txt')
# Summary table
print("-"*60)
print("1. Overall Performance")
print("-"*60)
print(f"{'Config':<15} {'mAP50-95':<12} {'mAP50':<12} {'Inf(ms)':<10} {'FPS':<10}")
print("-"*60)
for name, data in [("INT8_640p", int8), ("FP16_480p", fp16)]:
m = data.get('mAP50_95', 0)
m50 = data.get('mAP50', 0)
inf = data.get('inference', 0)
fps = data.get('fps', 0)
print(f"{name:<15} {m:<12.4f} {m50:<12.4f} {inf:<10.2f} {fps:<10.1f}")
print()
print("-"*60)
print("2. Person Detection (Class 0)")
print("-"*60)
print(f"{'Config':<15} {'P':<10} {'R':<10} {'AP50':<12} {'AP50-95':<12}")
print("-"*60)
for name, data in [("INT8_640p", int8), ("FP16_480p", fp16)]:
p = data.get('person', {}).get('P', 0)
r = data.get('person', {}).get('R', 0)
ap50 = data.get('person', {}).get('AP50', 0)
ap5095 = data.get('person', {}).get('AP50_95', 0)
print(f"{name:<15} {p:<10.3f} {r:<10.3f} {ap50:<12.4f} {ap5095:<12.4f}")
print()
print("-"*60)
print("3. Car Detection (Class 2)")
print("-"*60)
print(f"{'Config':<15} {'P':<10} {'R':<10} {'AP50':<12} {'AP50-95':<12}")
print("-"*60)
for name, data in [("INT8_640p", int8), ("FP16_480p", fp16)]:
p = data.get('car', {}).get('P', 0)
r = data.get('car', {}).get('R', 0)
ap50 = data.get('car', {}).get('AP50', 0)
ap5095 = data.get('car', {}).get('AP50_95', 0)
print(f"{name:<15} {p:<10.3f} {r:<10.3f} {ap50:<12.4f} {ap5095:<12.4f}")
print()
print("-"*60)
print("4. Speed Comparison")
print("-"*60)
for name, data in [("INT8_640p", int8), ("FP16_480p", fp16)]:
inf = data.get('inference', 0)
fps = data.get('fps', 0)
print(f" {name}: {inf:.2f}ms ({fps:.1f} FPS)")
print()
print("="*60)
print("Done!")
print("="*60)

46
prepare_coco.py Normal file
View File

@@ -0,0 +1,46 @@
import zipfile
import os
from pathlib import Path
def unzip_with_python(zip_path, dest_dir):
"""使用Python解压zip文件"""
zip_path = Path(zip_path)
dest_dir = Path(dest_dir)
print(f"Unzipping {zip_path} to {dest_dir}...")
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall(dest_dir)
print(f"Done! Extracted {len(zip_ref.namelist())} files")
def generate_image_list(img_dir, output_file):
"""生成图片路径列表文件"""
img_dir = Path(img_dir)
output_file = Path(output_file)
img_files = sorted(img_dir.glob("*.jpg"))
print(f"Found {len(img_files)} images in {img_dir}")
with open(output_file, 'w') as f:
for img_path in img_files:
f.write(str(img_path) + '\n')
print(f"Generated {output_file} with {len(img_files)} entries")
if __name__ == "__main__":
base_dir = Path(r"C:\Users\16337\PycharmProjects\Security\datasets\coco")
# 解压 val2017.zip
val_zip = base_dir / "images" / "val2017.zip"
val_dir = base_dir / "images" / "val2017"
if val_zip.exists() and not any(val_dir.glob("*.jpg")):
unzip_with_python(val_zip, base_dir / "images")
# 生成 val2017.txt
generate_image_list(val_dir, base_dir / "val2017.txt")
print("\nDone! Now you can run:")
print('yolo val model=yolo11n_int8_b1_8.engine data=coco.yaml imgsz=640 rect=False')

273
quantize_yolo.py Normal file
View File

@@ -0,0 +1,273 @@
import os
import glob
import numpy as np
import cv2
import tensorrt as trt
import pycuda.driver as cuda
import pycuda.autoinit
from ultralytics import YOLO
import logging
# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class DataLoader:
def __init__(self, data_dir, batch_size, input_shape):
self.batch_size = batch_size
self.input_shape = input_shape # (C, H, W)
self.img_paths = glob.glob(os.path.join(data_dir, '**', '*.jpg'), recursive=True)
if not self.img_paths:
self.img_paths = glob.glob(os.path.join(data_dir, '*.jpg'), recursive=False)
logger.info(f"Found {len(self.img_paths)} images for calibration in {data_dir}")
self.batch_idx = 0
self.max_batches = len(self.img_paths) // self.batch_size
# 预分配内存
self.calibration_data = np.zeros((self.batch_size, *self.input_shape), dtype=np.float32)
def reset(self):
self.batch_idx = 0
def next_batch(self):
if self.batch_idx >= self.max_batches:
return None
start = self.batch_idx * self.batch_size
end = start + self.batch_size
batch_paths = self.img_paths[start:end]
for i, path in enumerate(batch_paths):
img = cv2.imread(path)
if img is None:
continue
# Letterbox resize (Keep aspect ratio, padding)
img = self.preprocess(img, (self.input_shape[1], self.input_shape[2]))
# BGR to RGB, HWC to CHW, Normalize 0-1
img = img[:, :, ::-1].transpose(2, 0, 1)
img = np.ascontiguousarray(img, dtype=np.float32) / 255.0
self.calibration_data[i] = img
self.batch_idx += 1
return np.ascontiguousarray(self.calibration_data.ravel())
def preprocess(self, img, new_shape=(640, 640), color=(114, 114, 114)):
shape = img.shape[:2] # current shape [height, width]
if isinstance(new_shape, int):
new_shape = (new_shape, new_shape)
r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
r = min(r, 1.0) # only scale down
new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]
dw /= 2
dh /= 2
if shape[::-1] != new_unpad:
img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)
top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)
return img
class YOLOEntropyCalibrator(trt.IInt8EntropyCalibrator2):
def __init__(self, data_loader, cache_file='yolo_int8.cache'):
super().__init__()
self.data_loader = data_loader
self.cache_file = cache_file
self.d_input = cuda.mem_alloc(data_loader.calibration_data.nbytes)
self.data_loader.reset()
def get_batch_size(self):
return self.data_loader.batch_size
def get_batch(self, names):
try:
batch = self.data_loader.next_batch()
if batch is None:
return None
cuda.memcpy_htod(self.d_input, batch)
return [int(self.d_input)]
except Exception as e:
logger.error(f"Error in get_batch: {e}")
return None
def read_calibration_cache(self):
if os.path.exists(self.cache_file):
logger.info(f"Reading calibration cache from {self.cache_file}")
with open(self.cache_file, "rb") as f:
return f.read()
return None
def write_calibration_cache(self, cache):
logger.info(f"Writing calibration cache to {self.cache_file}")
with open(self.cache_file, "wb") as f:
f.write(cache)
def build_engine(onnx_path, engine_path, data_dir):
logger.info("Building TensorRT Engine...")
TRT_LOGGER = trt.Logger(trt.Logger.INFO)
builder = trt.Builder(TRT_LOGGER)
network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH))
config = builder.create_builder_config()
parser = trt.OnnxParser(network, TRT_LOGGER)
# 1. Parse ONNX
with open(onnx_path, 'rb') as model:
if not parser.parse(model.read()):
for error in range(parser.num_errors):
logger.error(parser.get_error(error))
return None
# 2. Config Builder
# Memory pool limit (e.g. 4GB)
config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, 4 * 1 << 30)
# INT8 Mode
if builder.platform_has_fast_int8:
logger.info("INT8 mode enabled.")
config.set_flag(trt.BuilderFlag.INT8)
# Calibration
calib_loader = DataLoader(data_dir, batch_size=8, input_shape=(3, 640, 640))
config.int8_calibrator = YOLOEntropyCalibrator(calib_loader)
else:
logger.warning("INT8 not supported on this platform. Falling back to FP16/FP32.")
# FP16 Mode (Mixed precision)
if builder.platform_has_fast_fp16:
config.set_flag(trt.BuilderFlag.FP16)
# 3. Dynamic Shapes
profile = builder.create_optimization_profile()
input_name = network.get_input(0).name
logger.info(f"Input Name: {input_name}")
# Min: 1, Opt: 4, Max: 8
profile.set_shape(input_name, (1, 3, 640, 640), (4, 3, 640, 640), (8, 3, 640, 640))
config.add_optimization_profile(profile)
# 4. Build Serialized Engine
try:
serialized_engine = builder.build_serialized_network(network, config)
if serialized_engine:
with open(engine_path, 'wb') as f:
f.write(serialized_engine)
logger.info(f"Engine saved to {engine_path}")
return engine_path
except Exception as e:
logger.error(f"Build failed: {e}")
return None
def export_onnx(model_name='yolo11n.pt'):
model = YOLO(model_name)
logger.info(f"Exporting {model_name} to ONNX...")
# 导出 dynamic=True 以支持动态 batch
path = model.export(format='onnx', dynamic=True, simplify=True, opset=12)
return path
def validate_models(pt_model_path, engine_path, data_yaml='coco8.yaml'):
logger.info("="*60)
logger.info("Model Validation and mAP Drop Calculation")
logger.info("="*60)
map_results = {}
logger.info("=== Validating FP32 (PyTorch) ===")
model_pt = YOLO(pt_model_path)
try:
metrics_pt = model_pt.val(data=data_yaml, batch=1, imgsz=640, rect=False)
map50_95_pt = metrics_pt.box.map
map50_pt = metrics_pt.box.map50
logger.info(f"FP32 mAP50-95: {map50_95_pt:.4f}")
logger.info(f"FP32 mAP50: {map50_pt:.4f}")
map_results['fp32'] = {
'map50_95': map50_95_pt,
'map50': map50_pt
}
except Exception as e:
logger.warning(f"FP32 Validation failed: {e}")
map_results['fp32'] = {'map50_95': 0.0, 'map50': 0.0}
logger.info("")
logger.info("=== Validating INT8 (TensorRT) ===")
model_trt = YOLO(engine_path, task='detect')
try:
metrics_trt = model_trt.val(data=data_yaml, batch=1, imgsz=640, rect=False)
map50_95_trt = metrics_trt.box.map
map50_trt = metrics_trt.box.map50
logger.info(f"INT8 mAP50-95: {map50_95_trt:.4f}")
logger.info(f"INT8 mAP50: {map50_trt:.4f}")
map_results['int8'] = {
'map50_95': map50_95_trt,
'map50': map50_trt
}
except Exception as e:
logger.warning(f"INT8 Validation failed: {e}")
map_results['int8'] = {'map50_95': 0.0, 'map50': 0.0}
logger.info("")
logger.info("="*60)
logger.info("mAP Drop Analysis")
logger.info("="*60)
if map_results['fp32']['map50_95'] > 0 and map_results['int8']['map50_95'] > 0:
drop_50_95 = (map_results['fp32']['map50_95'] - map_results['int8']['map50_95']) / map_results['fp32']['map50_95'] * 100
drop_50 = (map_results['fp32']['map50'] - map_results['int8']['map50']) / map_results['fp32']['map50'] * 100
logger.info(f"mAP50-95 Drop: {drop_50_95:.2f}%")
logger.info(f"mAP50 Drop: {drop_50:.2f}%")
logger.info("")
logger.info("Score Comparison:")
logger.info(f" FP32 mAP50-95: {map_results['fp32']['map50_95']:.4f} -> INT8 mAP50-95: {map_results['int8']['map50_95']:.4f}")
logger.info(f" FP32 mAP50: {map_results['fp32']['map50']:.4f} -> INT8 mAP50: {map_results['int8']['map50']:.4f}")
map_results['drop'] = {
'map50_95': drop_50_95,
'map50': drop_50
}
else:
logger.warning("Could not calculate mAP drop due to missing validation results")
map_results['drop'] = {'map50_95': 0.0, 'map50': 0.0}
logger.info("="*60)
return map_results
def main():
model_name = 'yolo11n.pt'
onnx_path = 'yolo11n.onnx'
engine_path = 'yolo11n.engine'
data_dir = 'data' # 校准数据目录
val_data_yaml = 'coco8.yaml' # 验证集配置,用于计算 mAP
# 1. 导出 ONNX
if not os.path.exists(onnx_path):
onnx_path = export_onnx(model_name)
else:
logger.info(f"Found existing ONNX: {onnx_path}")
# 2. 构建 TensorRT Engine (含 INT8 校准)
if not os.path.exists(engine_path):
# 检查 data 目录是否有图片
if not glob.glob(os.path.join(data_dir, '**', '*.jpg'), recursive=True) and \
not glob.glob(os.path.join(data_dir, '*.jpg'), recursive=False):
logger.error(f"No images found in {data_dir} for calibration! Please prepare data first.")
return
build_engine(onnx_path, engine_path, data_dir)
else:
logger.info(f"Found existing Engine: {engine_path}. Skipping build.")
# 3. 验证与对比
logger.info("Starting Validation... (Ensure you have a valid dataset yaml)")
validate_models(model_name, engine_path, val_data_yaml)
if __name__ == "__main__":
main()

33
rebuild_engines.bat Normal file
View File

@@ -0,0 +1,33 @@
@echo off
echo ============================================
echo Rebuilding TensorRT engines with batch 1-8
echo ============================================
call conda activate yolo
echo.
echo [1/5] Building INT8 640p (batch 1-8)...
trtexec --onnx=yolo11n.onnx --saveEngine=yolo11n_int8_b1_8.engine --explicitBatch --int8 --fp16 --workspace=4096 --builderOptimizationLevel=4 --calib=yolo11n_int8.cache --minShapes=input:1x3x640x640 --optShapes=input:4x3x640x640 --maxShapes=input:8x3x640x640 --useCudaGraph --useSpinWait --noTF32
echo Done.
echo.
echo [2/5] Building FP16 640p (batch 1-8)...
trtexec --onnx=yolo11n.onnx --saveEngine=yolo11n_fp16_640.engine --explicitBatch --fp16 --workspace=4096 --builderOptimizationLevel=4 --minShapes=input:1x3x640x640 --optShapes=input:4x3x640x640 --maxShapes=input:8x3x640x640 --useCudaGraph --useSpinWait --noTF32
echo Done.
echo.
echo [3/5] Building INT8 480p (batch 1-8)...
trtexec --onnx=yolo11n.onnx --saveEngine=yolo11n_int8_480.engine --explicitBatch --int8 --fp16 --workspace=4096 --builderOptimizationLevel=4 --calib=yolo11n_int8_480.cache --minShapes=input:1x3x480x480 --optShapes=input:4x3x480x480 --maxShapes=input:8x3x480x480 --useCudaGraph --useSpinWait --noTF32
echo Done.
echo.
echo [4/5] Building FP16 480p (batch 1-8)...
trtexec --onnx=yolo11n.onnx --saveEngine=yolo11n_fp16_480.engine --explicitBatch --fp16 --workspace=4096 --builderOptimizationLevel=4 --minShapes=input:1x3x480x480 --optShapes=input:4x3x480x480 --maxShapes=input:8x3x480x480 --useCudaGraph --useSpinWait --noTF32
echo Done.
echo.
echo ============================================
echo All engines rebuilt!
echo Now run: run_all_benchmarks.bat
echo ============================================
pause

28
rebuild_fp16.bat Normal file
View File

@@ -0,0 +1,28 @@
@echo off
echo ============================================
echo Rebuilding FP16 engines with batch=1 support
echo ============================================
call conda activate yolo
echo.
echo [1/3] Rebuilding FP16 640p (batch 1-8)...
trtexec --onnx=yolo11n.onnx --saveEngine=yolo11n_fp16_640.engine --explicitBatch --fp16 --workspace=4096 --builderOptimizationLevel=4 --minShapes=input:1x3x640x640 --optShapes=input:4x3x640x640 --maxShapes=input:8x3x640x640 --useCudaGraph --useSpinWait --noTF32
echo Done.
echo.
echo [2/3] Rebuilding FP16 480p (batch 1-8)...
trtexec --onnx=yolo11n.onnx --saveEngine=yolo11n_fp16_480.engine --explicitBatch --fp16 --workspace=4096 --builderOptimizationLevel=4 --minShapes=input:1x3x480x480 --optShapes=input:4x3x480x480 --maxShapes=input:8x3x480x480 --useCudaGraph --useSpinWait --noTF32
echo Done.
echo.
echo [3/3] Rebuilding INT8 640p (batch 1-8)...
trtexec --onnx=yolo11n.onnx --saveEngine=yolo11n_int8_b1_8.engine --explicitBatch --int8 --fp16 --workspace=4096 --builderOptimizationLevel=4 --calib=yolo11n_int8.cache --minShapes=input:1x3x640x640 --optShapes=input:4x3x640x640 --maxShapes=input:8x3x640x640 --useCudaGraph --useSpinWait --noTF32
echo Done.
echo.
echo ============================================
echo All engines rebuilt!
echo Now run: run_correct_benchmark.bat
echo ============================================
pause

29
run_all_benchmarks.bat Normal file
View File

@@ -0,0 +1,29 @@
@echo off
echo ============================================
echo YOLO11n Person and Vehicle Detection Benchmark
echo ============================================
call conda activate yolo
echo [1/4] Testing FP32...
yolo val model=yolo11n.pt data=coco_person_vehicle.yaml imgsz=640 rect=False batch=1 > fp32_results.txt 2>&1
echo Done: fp32_results.txt
echo [2/4] Testing INT8 640p...
yolo val model=yolo11n_int8_b1_8.engine data=coco_person_vehicle.yaml imgsz=640 rect=False batch=1 > int8_640_results.txt 2>&1
echo Done: int8_640_results.txt
echo [3/4] Testing FP16 640p...
yolo val model=yolo11n_fp16_640.engine data=coco_person_vehicle.yaml imgsz=640 rect=False batch=1 > fp16_640_results.txt 2>&1
echo Done: fp16_640_results.txt
echo [4/4] Testing FP16 480p...
yolo val model=yolo11n_fp16_480.engine data=coco_person_vehicle.yaml imgsz=480 rect=False batch=1 > fp16_480_results.txt 2>&1
echo Done: fp16_480_results.txt
echo.
echo ============================================
echo All validation tests completed!
echo Now run: python parse_results.py
echo ============================================
pause

34
run_correct_benchmark.bat Normal file
View File

@@ -0,0 +1,34 @@
@echo off
echo ============================================
echo YOLO11n Benchmark - Person & Vehicle Only
echo Classes: 0(person),1(bicycle),2(car),3(motorcycle),5(bus),7(truck)
echo ============================================
echo.
call conda activate yolo
echo [1/4] Testing FP32 (PyTorch)...
yolo val model=yolo11n.pt data=coco.yaml imgsz=640 batch=1 device=0 classes=0,1,2,3,5,7 > fp32_results.txt 2>&1
echo Done: fp32_results.txt
echo.
echo [2/4] Testing INT8 640p...
yolo val model=yolo11n_int8_b1_8.engine data=coco.yaml imgsz=640 batch=1 device=0 classes=0,1,2,3,5,7 > int8_640_results.txt 2>&1
echo Done: int8_640_results.txt
echo.
echo [3/4] Testing FP16 640p...
yolo val model=yolo11n_fp16_640.engine data=coco.yaml imgsz=640 batch=1 device=0 classes=0,1,2,3,5,7 > fp16_640_results.txt 2>&1
echo Done: fp16_640_results.txt
echo.
echo [4/4] Testing FP16 480p...
yolo val model=yolo11n_fp16_480.engine data=coco.yaml imgsz=480 batch=1 device=0 classes=0,1,2,3,5,7 > fp16_480_results.txt 2>&1
echo Done: fp16_480_results.txt
echo.
echo ============================================
echo All validation tests completed!
echo Now run: python parse_final.py
echo ============================================
pause

34
run_full_analysis.bat Normal file
View File

@@ -0,0 +1,34 @@
@echo off
echo ============================================
echo Complete Comparison Analysis
echo ============================================
echo.
call conda activate yolo
echo.
echo [1/4] Running all validation tests...
echo.
call run_all_benchmarks.bat
echo.
echo [2/4] Parsing results and generating report...
echo.
python parse_results.py
echo.
echo [3/4] Generating visualization charts...
echo.
python generate_charts.py
echo.
echo ============================================
echo [4/4] Done!
echo ============================================
echo.
echo Results saved to: vehicle_person_benchmark\
echo - final_report.txt (detailed report)
echo - benchmark_charts.png (charts)
echo - all_results.json (raw data)
pause

94
run_full_workflow.bat Normal file
View File

@@ -0,0 +1,94 @@
@echo off
chcp 65001 >nul
echo ============================================
echo YOLO INT8 Quantization Complete Workflow
echo ============================================
echo.
REM 激活虚拟环境
echo [1/4] Activating yolo virtual environment...
call conda activate yolo
echo.
REM 步骤1: 检查并导出ONNX模型
echo [2/4] Checking ONNX model...
if not exist yolo11n.onnx (
echo ONNX model not found. Exporting from yolo11n.pt...
if not exist yolo11n.pt (
echo ERROR: yolo11n.pt not found!
echo Please place yolo11n.pt in the current directory.
pause
exit /b 1
)
python -c "from ultralytics import YOLO; model = YOLO('yolo11n.pt'); model.export(format='onnx', dynamic=True, simplify=True, opset=12)"
if not exist yolo11n.onnx (
echo ERROR: ONNX export failed!
pause
exit /b 1
)
echo ONNX model exported successfully.
) else (
echo ONNX model already exists: yolo11n.onnx
)
echo.
REM 步骤2: 生成校准缓存
echo [3/4] Generating calibration cache...
if not exist yolo11n_int8.cache (
echo Running calibration_gen.py...
python calibration_gen.py
if not exist yolo11n_int8.cache (
echo ERROR: Calibration cache generation failed!
pause
exit /b 1
)
echo Calibration cache generated: yolo11n_int8.cache
) else (
echo Calibration cache already exists: yolo11n_int8.cache
)
echo.
REM 步骤3: 构建TensorRT Engine
echo [4/4] Building TensorRT Engine...
echo.
echo Running trtexec with INT8 calibration...
echo.
trtexec ^
--onnx=yolo11n.onnx ^
--saveEngine=yolo11n_int8_b1_8.engine ^
--explicitBatch ^
--int8 ^
--fp16 ^
--workspace=4096 ^
--builderOptimizationLevel=5 ^
--profilingVerbosity=detailed ^
--calib=yolo11n_int8.cache ^
--minShapes=input:1x3x640x640 ^
--optShapes=input:4x3x640x640 ^
--maxShapes=input:8x3x640x640 ^
--useCudaGraph ^
--useSpinWait ^
--noTF32
if %errorlevel% equ 0 (
echo.
echo ============================================
echo TensorRT Engine built successfully!
echo Output: yolo11n_int8_b1_8.engine
echo ============================================
echo.
echo Next steps:
echo 1. Run: python quantize_yolo.py
echo to validate models and calculate mAP drop
echo.
) else (
echo.
echo ============================================
echo Engine build failed!
============================================
pause
exit /b 1
)
pause

13
test_engine_trtexec.bat Normal file
View File

@@ -0,0 +1,13 @@
@echo off
echo ============================================
echo Test FP16 480p engine with trtexec
echo ============================================
call conda activate yolo
echo [TEST] Direct inference with trtexec...
trtexec --loadEngine=yolo11n_fp16_480.engine --shapes=images:1x3x480x480 --iterations=10 --noDataTransfers --useCudaGraph
echo.
echo Done.
pause

17
test_speed_trtexec.bat Normal file
View File

@@ -0,0 +1,17 @@
@echo off
echo ============================================
echo TensorRT Engine Benchmark
echo Run after: conda activate yolo
echo ============================================
echo.
echo [1] INT8-640p Speed Test...
trtexec --loadEngine=yolo11n_int8_b1_8.engine --shapes=images:1x3x640x640 --iterations=100
echo.
echo [2] FP16-480p Speed Test...
trtexec --loadEngine=yolo11n_fp16_480.engine --shapes=images:1x3x480x480 --iterations=100
echo.
echo Done.
pause

17
test_two_engines.bat Normal file
View File

@@ -0,0 +1,17 @@
@echo off
echo ============================================
echo Build FP16 480p + Test FP16 480p
echo ============================================
call conda activate yolo
echo [1/2] Building FP16 480p engine...
trtexec --onnx=yolo11n.onnx --saveEngine=yolo11n_fp16_480.engine --explicitBatch --fp16 --workspace=4096 --builderOptimizationLevel=4 --minShapes=images:1x3x480x480 --optShapes=images:4x3x480x480 --maxShapes=images:8x3x480x480 --useCudaGraph --useSpinWait --noTF32
echo.
echo [2/2] Testing FP16 480p...
yolo val model=yolo11n_fp16_480.engine data=coco.yaml imgsz=480 batch=1 device=0 classes=0,1,2,3,5,7 > fp16_480_results.txt 2>&1
echo.
echo Done. Run: python parse_simple.py
pause

42
validate_int8.bat Normal file
View File

@@ -0,0 +1,42 @@
@echo off
chcp 65001 >nul
echo ============================================
echo YOLO INT8 TensorRT Validation Script
echo ============================================
echo.
REM 激活虚拟环境
echo Activating yolo virtual environment...
call conda activate yolo
echo.
REM 检查engine文件
if not exist yolo11n_int8_b1_8.engine (
echo ERROR: yolo11n_int8_b1_8.engine not found!
echo Please build the engine first using: build_engine.bat
pause
exit /b 1
)
echo Validating INT8 TensorRT Engine with COCO dataset...
echo Engine: yolo11n_int8_b1_8.engine
echo.
REM 使用rect=False确保输入尺寸为640x640
yolo val model=yolo11n_int8_b1_8.engine data=coco.yaml imgsz=640 rect=False
if %errorlevel% equ 0 (
echo.
echo ============================================
echo Validation completed successfully!
echo ============================================
) else (
echo.
echo ============================================
echo Validation failed!
echo ============================================
pause
exit /b 1
)
pause