news 2026/3/20 4:27:36

EagleEye代码实例:Python调用EagleEye API实现批量图像检测与结果解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
EagleEye代码实例:Python调用EagleEye API实现批量图像检测与结果解析

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.ThreadPoolExecutormax_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友好格式)

filenamelabelconfidencex1_normy1_normx2_normy2_normx1_pxy1_pxx2_pxy2_px
product_a.jpgdefect0.920.3210.4560.4120.523205292264335
product_a.jpgdefect0.880.6780.2100.7650.298434134490191
product_b.jpgscratch0.870.1230.7890.1980.85679505127548

所有列名用英文小写+下划线,符合数据分析惯例
confidence为float类型,可直接做阈值筛选(如df[df['confidence']>0.8]
像素坐标列名带_px后缀,一眼识别用途

4.3 你还能立刻做的3件事

  1. 统计缺陷分布

    df = pd.read_csv("./output_results/csv_results/detection_summary.csv") print(df['label'].value_counts()) # 输出:defect 2, scratch 1
  2. 提取高危缺陷图

    high_risk = df[df['label']=='defect'][['filename']].drop_duplicates() # 复制这些图到新文件夹:`cp $(cat high_risk.txt) ./urgent_review/`
  3. 生成日报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星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/15 14:58:17

Z-Image-ComfyUI+ControlNet,精准控制生成

Z-Image-ComfyUIControlNet,精准控制生成 在图像生成领域,“画得像”只是起点,“控得住”才是专业级应用的核心门槛。设计师常遇到这样的困境:输入“穿青花瓷纹样旗袍的女子站在苏州园林月洞门前”,模型却把旗袍纹样错…

作者头像 李华
网站建设 2026/3/12 3:45:04

LeagueAkari:重新定义英雄联盟辅助工具的游戏体验优化方案

LeagueAkari:重新定义英雄联盟辅助工具的游戏体验优化方案 【免费下载链接】LeagueAkari ✨兴趣使然的,功能全面的英雄联盟工具集。支持战绩查询、自动秒选等功能。基于 LCU API。 项目地址: https://gitcode.com/gh_mirrors/le/LeagueAkari 英雄…

作者头像 李华
网站建设 2026/3/15 9:57:01

时间戳命名防覆盖,输出文件管理更规范

时间戳命名防覆盖,输出文件管理更规范 在使用 OCR 文字检测模型处理图片时,一个看似微小却极易被忽视的问题常常带来不小困扰:多次运行后结果文件被反复覆盖,历史记录丢失,调试无从追溯。尤其在批量检测、A/B 阈值对比…

作者头像 李华
网站建设 2026/3/20 2:21:35

5款资源提取浏览器工具横评:哪款能真正解决你的视频下载难题?

5款资源提取浏览器工具横评:哪款能真正解决你的视频下载难题? 【免费下载链接】cat-catch 猫抓 chrome资源嗅探扩展 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 在数字内容爆炸的时代,网页媒体捕获已成为高效获取信…

作者头像 李华
网站建设 2026/3/14 5:06:59

显存不足怎么办?Live Avatar低配版运行策略

显存不足怎么办?Live Avatar低配版运行策略 1. 问题本质:为什么24GB显卡跑不动Live Avatar? 你是不是也遇到过这样的情况:手握5张RTX 4090,每张24GB显存,信心满满地想跑通Live Avatar,结果启动…

作者头像 李华