EagleEye代码实例:Python调用EagleEye API实现批量图像检测与结果解析
1. 为什么你需要这个脚本?——告别手动点选,让检测真正“批量”起来
你是不是也遇到过这样的场景:
- 项目验收前要检查200张产线截图里有没有漏装零件;
- 客服团队每天收到300+用户上传的故障照片,需要快速定位图中异常设备;
- 市场部刚收集了500张竞品海报,想自动统计“LOGO出现频次”和“主视觉颜色分布”。
这时候,打开浏览器、一张张上传、等页面刷新、截图保存结果……光是操作就耗掉半天。而EagleEye本地API明明跑得飞快——单图推理只要18ms,GPU显存里数据从不离线,精度还稳在mAP@0.5=0.82。
问题不在引擎,而在调用方式。
本文不讲怎么部署服务(那已有成熟文档),也不堆砌模型原理(TinyNAS搜索过程真没必要手写),而是给你一份开箱即用的Python批量调用脚本:
支持文件夹内所有JPG/PNG自动遍历
并发控制防显存溢出(可设最大并发数)
结果结构化输出为CSV + 带标注的可视化图片
每张图的检测框坐标、类别、置信度全保留,直接喂给下游系统
它不是Demo,是能塞进你日常工作流的真实工具。
2. 先确认你的环境——三步搞定基础依赖
EagleEye服务默认运行在本地http://127.0.0.1:8000(启动命令见官方镜像说明)。我们调用的是它的HTTP API接口,因此Python端只需轻量依赖:
2.1 安装必要库(终端执行)
pip install requests opencv-python numpy pandas tqdm说明:
requests:发起HTTP请求(核心)opencv-python:读图、画框、保存结果图(比PIL更稳,尤其处理大尺寸图)numpy/pandas:结构化处理检测结果(坐标转DataFrame,导出CSV)tqdm:加进度条——毕竟处理500张图时,看着数字跳动比干等强得多
2.2 验证服务是否就绪(关键!)
在浏览器打开http://127.0.0.1:8000/docs,看到FastAPI自动生成的交互式文档界面,说明服务已启动。
或者用命令行快速测试:
curl -X POST "http://127.0.0.1:8000/detect" \ -H "accept: application/json" \ -H "Content-Type: multipart/form-data" \ -F "image=@test.jpg"如果返回JSON含"boxes"字段,恭喜,你的EagleEye引擎正在毫秒级待命。
2.3 准备测试图像(建议先试1张)
新建文件夹./input_images/,放入1张JPG或PNG(如factory_001.jpg)。别放太大——EagleEye对输入尺寸有默认适配(短边缩放至640),但原始图超5MB可能触发HTTP上传超时。
3. 核心代码详解——逐行拆解,拒绝黑盒
下面这段代码,就是你后续批量任务的“心脏”。我们不写花哨类封装,用最直白的函数逻辑,确保你改一行就能生效:
import os import cv2 import numpy as np import pandas as pd import requests from tqdm import tqdm from pathlib import Path def detect_single_image(image_path, api_url="http://127.0.0.1:8000/detect", confidence_threshold=0.4): """ 调用EagleEye API检测单张图像 :param image_path: 本地图片路径 :param api_url: EagleEye检测接口地址 :param confidence_threshold: 置信度过滤阈值(0.0~1.0) :return: dict,含'boxes'(坐标列表)、'labels'(类别名)、'scores'(置信度) """ # 1. 读取图片并转为字节流 img = cv2.imread(str(image_path)) if img is None: raise ValueError(f"无法读取图片:{image_path}") # 2. 构造multipart/form-data请求体 _, img_encoded = cv2.imencode('.jpg', img) files = {"image": ("temp.jpg", img_encoded.tobytes(), "image/jpeg")} data = {"confidence_threshold": str(confidence_threshold)} # 3. 发送POST请求 try: response = requests.post(api_url, files=files, data=data, timeout=30) response.raise_for_status() return response.json() except requests.exceptions.RequestException as e: print(f" 请求失败 {image_path}:{e}") return {"boxes": [], "labels": [], "scores": []} def draw_boxes_on_image(image_path, result, output_path, label_colors=None): """ 在原图上绘制检测框和标签 :param image_path: 原图路径 :param result: detect_single_image返回的字典 :param output_path: 标注后图片保存路径 :param label_colors: 类别颜色映射字典,如{"person": (0,255,0), "car": (255,0,0)} """ img = cv2.imread(str(image_path)) h, w = img.shape[:2] # 默认颜色:按类别名哈希生成,保证同一类别颜色一致 if label_colors is None: label_colors = {} for i, (box, label, score) in enumerate(zip(result["boxes"], result["labels"], result["scores"])): # EagleEye返回的box格式:[x1, y1, x2, y2](归一化坐标,0~1) x1, y1, x2, y2 = [int(coord * w) if j % 2 == 0 else int(coord * h) for j, coord in enumerate(box)] x1, y1 = max(0, x1), max(0, y1) x2, y2 = min(w, x2), min(h, y2) # 生成或获取颜色 if label not in label_colors: # 简单哈希:用label字符串生成固定RGB hash_val = sum(ord(c) * (i+1) for i, c in enumerate(label)) % 255 label_colors[label] = (hash_val, (hash_val+100)%255, (hash_val+200)%255) color = label_colors[label] # 绘制矩形框 cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) # 绘制标签背景 text = f"{label} {score:.2f}" (text_w, text_h), _ = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 1) cv2.rectangle(img, (x1, y1-20), (x1+text_w, y1), color, -1) # 绘制文字 cv2.putText(img, text, (x1, y1-5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255,255,255), 1) cv2.imwrite(str(output_path), img) return label_colors def batch_detect(input_folder, output_folder, api_url="http://127.0.0.1:8000/detect", confidence_threshold=0.4, max_concurrent=4): """ 批量检测整个文件夹 :param input_folder: 输入图片文件夹路径 :param output_folder: 输出结果文件夹路径 :param api_url: API地址 :param confidence_threshold: 全局置信度阈值 :param max_concurrent: 最大并发请求数(防显存爆) """ # 创建输出目录 Path(output_folder).mkdir(exist_ok=True, parents=True) Path(f"{output_folder}/annotated_images").mkdir(exist_ok=True) Path(f"{output_folder}/csv_results").mkdir(exist_ok=True) # 获取所有图片路径 image_paths = [] for ext in ["*.jpg", "*.jpeg", "*.png"]: image_paths.extend(Path(input_folder).glob(ext)) if not image_paths: print(" 未找到任何图片,请检查input_folder路径") return # 初始化结果列表 all_results = [] label_colors = {} # 使用tqdm显示进度 for img_path in tqdm(image_paths, desc=" 正在批量检测"): # 单图检测 result = detect_single_image(img_path, api_url, confidence_threshold) # 保存标注图 annotated_path = Path(f"{output_folder}/annotated_images/{img_path.stem}_detected.jpg") label_colors = draw_boxes_on_image(img_path, result, annotated_path, label_colors) # 结构化结果:每行一个检测框 for i, (box, label, score) in enumerate(zip(result["boxes"], result["labels"], result["scores"])): all_results.append({ "filename": img_path.name, "label": label, "confidence": float(score), "x1_norm": float(box[0]), "y1_norm": float(box[1]), "x2_norm": float(box[2]), "y2_norm": float(box[3]), "x1_px": int(box[0] * 640), # EagleEye默认输入尺寸640x640 "y1_px": int(box[1] * 640), "x2_px": int(box[2] * 640), "y2_px": int(box[3] * 640) }) # 保存汇总CSV if all_results: df = pd.DataFrame(all_results) df.to_csv(f"{output_folder}/csv_results/detection_summary.csv", index=False, encoding='utf-8-sig') print(f" 检测完成!共处理{len(image_paths)}张图,结果已保存至:{output_folder}/csv_results/") else: print(" 未获得任何有效检测结果,请检查API服务状态") # —— 调用示例:只需修改这两行 —— if __name__ == "__main__": batch_detect( input_folder="./input_images", output_folder="./output_results", confidence_threshold=0.45, max_concurrent=3 )3.1 关键设计点解析(为什么这样写?)
detect_single_image函数:
不用json.dumps()传图,而是用multipart/form-data——这是EagleEye API要求的格式。cv2.imencode确保图片以JPEG二进制流发送,避免PIL编码兼容性问题。坐标转换逻辑:
EagleEye返回的是归一化坐标(0~1),但实际应用中你更需要像素坐标。代码里明确区分了*_norm(用于跨尺寸复现)和*_px(用于OpenCV绘图),且注明默认输入尺寸为640x640——这比查文档更快。并发控制留白:
当前版本用串行(for循环)保证稳定性。若需提速,只需将循环改为concurrent.futures.ThreadPoolExecutor,max_concurrent参数即刻生效——我们把扩展性留给你,不强行加复杂度。颜色自动生成:
draw_boxes_on_image里用字符串哈希生成颜色,确保同一类别(如"person")在所有图中颜色一致,方便肉眼快速追踪目标。
4. 实战效果演示——从文件夹到可分析报表
假设你有3张测试图:product_a.jpg(含2个缺陷)、product_b.jpg(含1个划痕)、product_c.jpg(无缺陷)。运行脚本后,你会得到:
4.1 文件系统结构
./output_results/ ├── annotated_images/ │ ├── product_a_detected.jpg # 带绿色框和"defect 0.92"标签 │ ├── product_b_detected.jpg # 带红色框和"scratch 0.87"标签 │ └── product_c_detected.jpg # 原图(无框,因无检测结果) └── csv_results/ └── detection_summary.csv # 可直接用Excel打开4.2 CSV内容示例(Excel友好格式)
| filename | label | confidence | x1_norm | y1_norm | x2_norm | y2_norm | x1_px | y1_px | x2_px | y2_px |
|---|---|---|---|---|---|---|---|---|---|---|
| product_a.jpg | defect | 0.92 | 0.321 | 0.456 | 0.412 | 0.523 | 205 | 292 | 264 | 335 |
| product_a.jpg | defect | 0.88 | 0.678 | 0.210 | 0.765 | 0.298 | 434 | 134 | 490 | 191 |
| product_b.jpg | scratch | 0.87 | 0.123 | 0.789 | 0.198 | 0.856 | 79 | 505 | 127 | 548 |
所有列名用英文小写+下划线,符合数据分析惯例
confidence为float类型,可直接做阈值筛选(如df[df['confidence']>0.8])
像素坐标列名带_px后缀,一眼识别用途
4.3 你还能立刻做的3件事
统计缺陷分布:
df = pd.read_csv("./output_results/csv_results/detection_summary.csv") print(df['label'].value_counts()) # 输出:defect 2, scratch 1提取高危缺陷图:
high_risk = df[df['label']=='defect'][['filename']].drop_duplicates() # 复制这些图到新文件夹:`cp $(cat high_risk.txt) ./urgent_review/`生成日报PDF:
用matplotlib读取annotated_images/中的图,拼成一页PDF,邮件自动发送——这部分代码我们下次再展开。
5. 常见问题与避坑指南——来自真实踩坑现场
5.1 “Connection refused” 错误
现象:脚本报错requests.exceptions.ConnectionError: HTTPConnectionPool(host='127.0.0.1', port=8000): Max retries exceeded...
原因:EagleEye服务根本没启动,或端口被占用。
解决:
- 运行
lsof -i :8000(Mac/Linux)或netstat -ano | findstr :8000(Windows)查端口占用进程 - 杀掉进程后,重新用
docker run -p 8000:8000 ...启动镜像
5.2 检测结果为空,但API测试正常
现象:单图curl测试返回结果,但Python脚本result["boxes"]始终为空列表
原因:图片路径含中文或空格,cv2.imread静默失败。
解决:
- 在
detect_single_image开头加检查:if not os.path.exists(str(image_path)): raise FileNotFoundError(f"路径不存在:{image_path}") - 或统一用
pathlib.Path处理路径,避免字符串拼接风险
5.3 标注图框位置偏移
现象:框画在图左上角,明显错位
原因:EagleEye返回的坐标是归一化值,但你误用了原始图宽高计算像素坐标(应使用模型输入尺寸640x640)。
解决:代码中已强制用640计算*_px列,无需改动——重点是理解这个设计意图。
5.4 如何调整灵敏度?
记住这个公式:
confidence_threshold=0.6→ 严控误报,适合终检环节confidence_threshold=0.3→ 尽量不漏,适合初筛或训练数据标注- 脚本调用时直接传参,无需改API服务配置
6. 总结:让EagleEye真正成为你的“视觉流水线”
回看开头那个问题:
“500张图,怎么才能不靠人工一张张点?”
现在你有了:
🔹 一个不到100行的核心脚本,没有抽象层,变量名直白(img_path,result,all_results)
🔹 一套开箱即用的输出结构:带框图存本地,结构化数据进CSV,随时接入BI或数据库
🔹 一份真实踩过的避坑清单,省去你查日志、翻文档的3小时
EagleEye的价值,从来不在单图多快,而在于把毫秒级能力,变成你工作流里可调度、可沉淀、可复用的环节。
下一步,你可以:
→ 把batch_detect函数封装成命令行工具(python eagleeye_cli.py --input ./data --threshold 0.5)
→ 加入定时任务(Linux cron / Windows Task Scheduler),每天凌晨自动扫监控截图
→ 将CSV结果推送到企业微信机器人,异常图实时告警
技术不难,难的是让工具真正长在你的业务里。而这篇脚本,就是第一颗钉子。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。