EagleEye实操手册:EagleEye检测结果JSON Schema解析与结构化入库方案
1. 为什么需要解析EagleEye的JSON输出?
你刚跑通EagleEye,上传一张图,几毫秒后右侧面板弹出带框的识别结果——很酷。但如果你真正想用它做点实事,比如:
- 把每天识别出的2000个“未戴安全帽”事件存进数据库,生成日报;
- 把产线上的“缺陷零件”坐标和置信度同步到MES系统触发拦截;
- 在告警大屏上按区域、类别、时间维度聚合统计,而不是只看单张图;
那光靠Streamlit界面上那个漂亮的可视化结果远远不够。
真正的价值,藏在那一段被自动折叠、很少有人点开的JSON响应体里。
EagleEye返回的不是简单字符串,而是一份结构严谨、字段丰富、可编程消费的检测结果数据包。但它默认不解释字段含义,也不说明如何映射到业务表。本手册不讲怎么装CUDA、不教YOLO原理,只聚焦一件事:把EagleEye吐出来的JSON,变成你能存、能查、能分析、能对接下游系统的结构化数据。
我们全程用真实响应示例说话,所有代码可直接复制运行,所有字段都标注了“什么场景下必须用”“什么情况可以忽略”。
2. EagleEye检测结果JSON完整Schema详解
EagleEye的HTTP API(/detect端点)返回标准JSON对象。以下结构基于v1.3.2实测响应整理,已剔除调试字段,保留全部生产环境可用字段。
2.1 根对象结构概览
{ "status": "success", "timestamp": "2024-06-15T09:23:41.872Z", "image_id": "img_9a3f7c2e", "width": 1920, "height": 1080, "detections": [...], "summary": { ... } }| 字段 | 类型 | 是否必填 | 说明 | 实战建议 |
|---|---|---|---|---|
status | string | 固定为"success"或"error" | 务必校验:若非success,直接跳过后续解析,记录错误日志 | |
timestamp | string (ISO 8601) | 检测完成的UTC时间戳 | 存入数据库时建议转为本地时区或统一用UTC,避免时区混乱 | |
image_id | string | 用户上传时指定的ID,若未传则为系统生成UUID | 关键业务字段:用于关联原始图片路径、用户操作记录、审计追踪 | |
width/height | integer | 原图宽高(像素),非缩放后尺寸 | 计算坐标归一化、ROI裁剪、分辨率适配的基准值 | |
detections | array | 检测到的所有目标列表(可能为空数组) | 核心数据源:90%的业务逻辑从此数组展开 | |
summary | object | 统计摘要,含各类别数量、平均置信度等 | 适合快速生成报表,无需遍历detections |
小贴士:EagleEye不会在
status: "error"时返回detections字段。不要假设字段永远存在,写代码前先判空。
2.2 detections数组:每个目标的完整描述
detections是对象数组,每个元素代表一个被识别的目标。以下是单个检测项的完整结构:
{ "id": "det_5b8d2a1f", "category": "person", "bbox": [124.5, 87.2, 312.8, 495.6], "confidence": 0.924, "segmentation": [[125,88],[126,90],...], "keypoints": {"left_eye":[182.3,124.7],"right_eye":[201.1,123.9]}, "attributes": {"occluded": false, "truncated": true} }2.2.1 必用字段(80%场景都需提取)
| 字段 | 类型 | 说明 | 典型用途 | 注意事项 |
|---|---|---|---|---|
id | string | 本次检测中该目标的唯一ID(非全局) | 用于前端高亮联动、结果去重 | 同一图内不重复,跨图无意义 |
category | string | 预训练模型定义的类别名(如"person"、"hard_hat"、"fire_extinguisher") | 分类统计、规则引擎触发(如category=="person" and bbox[3]-bbox[1] < 200→ 判定为远距离小人) | 区分大小写,建议存为小写并建立映射表 |
bbox | array of 4 floats | 归一化边界框[x_min, y_min, x_max, y_max],值域[0,1] | 计算位置、面积、长宽比;叠加到原图需乘以width/height | 不是像素坐标!是相对比例,避免硬编码像素值 |
confidence | float | 模型对该检测结果的置信度(0~1) | 过滤低质量结果(如confidence > 0.5)、加权统计 | 精确到小数点后3位,足够业务使用 |
2.2.2 可选但高价值字段(按需启用)
| 字段 | 类型 | 说明 | 启用建议 | 示例场景 |
|---|---|---|---|---|
segmentation | array of arrays | 多边形分割掩码(像素坐标,非归一化) | 仅当需精确抠图、计算不规则区域面积时启用 | 安全帽佩戴检测中,精确计算帽子覆盖头顶比例 |
keypoints | object | 关键点坐标(如眼睛、鼻子),单位同bbox(归一化) | 需姿态分析、朝向判断时启用 | 判断人员是否面向摄像头、是否低头玩手机 |
attributes | object | 扩展属性,含occluded(遮挡)、truncated(截断)等布尔值 | 高精度场景必开,用于过滤不可靠检测 | 产线质检中,occluded==true的目标自动标记为“待复检” |
关键提醒:
segmentation和keypoints默认不返回!需在API请求中显式添加参数:{"return_segmentation": true, "return_keypoints": true}
否则字段不存在,代码中直接访问会报错。
2.3 summary对象:高效统计不遍历
当只需知道“这张图有几个工人、几个灭火器”,不必逐个处理detections时,summary是黄金字段:
"summary": { "total_count": 7, "by_category": { "person": 4, "hard_hat": 3, "safety_vest": 2 }, "avg_confidence": 0.862, "max_confidence": 0.941, "min_confidence": 0.723 }total_count: 总检测数(含所有类别)by_category: 按类别分组计数(注意:不是互斥统计,一个目标可能同时属于person和hard_hat)avg_confidence: 所有检测的平均置信度,快速评估整图质量
最佳实践:对海量图片做初步筛选时,先读summary.total_count和summary.avg_confidence,仅对total_count > 0 && avg_confidence > 0.7的图片再深入解析detections,性能提升3倍以上。
3. 结构化入库:从JSON到数据库的三步落地
解析清楚只是第一步。真正让数据产生价值,得让它稳稳躺在数据库里,并能被BI工具、告警系统随时调用。我们以PostgreSQL为例(MySQL/SQLite逻辑一致),给出生产级方案。
3.1 数据库表设计:兼顾查询效率与扩展性
-- 主检测记录表(每张图一条记录) CREATE TABLE eagleeye_detection_logs ( id SERIAL PRIMARY KEY, image_id VARCHAR(64) NOT NULL, -- 对应JSON中的image_id detected_at TIMESTAMPTZ NOT NULL, -- timestamp转为timestamptz width INTEGER NOT NULL, height INTEGER NOT NULL, total_detections INTEGER NOT NULL, avg_confidence NUMERIC(4,3) NOT NULL, created_at TIMESTAMPTZ DEFAULT NOW() ); -- 检测目标明细表(一对多,一张图对应多条目标) CREATE TABLE eagleeye_detections ( id SERIAL PRIMARY KEY, log_id INTEGER NOT NULL REFERENCES eagleeye_detection_logs(id) ON DELETE CASCADE, category VARCHAR(32) NOT NULL, confidence NUMERIC(4,3) NOT NULL, x_min NUMERIC(6,3) NOT NULL, -- bbox[0] * width y_min NUMERIC(6,3) NOT NULL, -- bbox[1] * height x_max NUMERIC(6,3) NOT NULL, -- bbox[2] * width y_max NUMERIC(6,3) NOT NULL, -- bbox[3] * height width_px NUMERIC(6,3) GENERATED ALWAYS AS (x_max - x_min) STORED, height_px NUMERIC(6,3) GENERATED ALWAYS AS (y_max - y_min) STORED, area_px NUMERIC(10,3) GENERATED ALWAYS AS ((x_max - x_min) * (y_max - y_min)) STORED, is_occluded BOOLEAN DEFAULT FALSE, is_truncated BOOLEAN DEFAULT FALSE, created_at TIMESTAMPTZ DEFAULT NOW() ); -- 添加索引(关键!否则查慢) CREATE INDEX idx_detection_logs_image_id ON eagleeye_detection_logs(image_id); CREATE INDEX idx_detections_log_id ON eagleeye_detections(log_id); CREATE INDEX idx_detections_category_conf ON eagleeye_detections(category, confidence); CREATE INDEX idx_detections_area ON eagleeye_detections(area_px);设计要点:
- 不存原始JSON:避免JSONB字段导致查询困难,所有业务字段拆解为独立列;
- 预计算衍生字段:
width_px、height_px、area_px用GENERATED ALWAYS自动计算,查询时无需SELECT x_max-x_min; - 索引精准覆盖:按最常查询条件建索引(如按
category查某类目标、按area_px查大目标); - 外键级联删除:删主记录时自动清理明细,避免脏数据。
3.2 Python入库脚本:健壮、可监控、零依赖
以下代码片段可直接集成到你的Flask/FastAPI服务中,或作为独立脚本运行:
import json import psycopg2 from psycopg2.extras import execute_batch from datetime import datetime, timezone def parse_and_store_detection_result(json_data: dict, db_conn): """解析EagleEye JSON并批量写入PostgreSQL""" try: # 步骤1:校验根结构 if json_data.get("status") != "success": raise ValueError(f"Detection failed: {json_data.get('error', 'unknown')}") # 步骤2:提取根信息 image_id = json_data["image_id"] timestamp = datetime.fromisoformat(json_data["timestamp"].rstrip("Z")).replace(tzinfo=timezone.utc) width, height = json_data["width"], json_data["height"] total_count = len(json_data["detections"]) avg_conf = json_data["summary"]["avg_confidence"] # 步骤3:插入主记录,获取自增ID with db_conn.cursor() as cur: cur.execute( "INSERT INTO eagleeye_detection_logs " "(image_id, detected_at, width, height, total_detections, avg_confidence) " "VALUES (%s, %s, %s, %s, %s, %s) RETURNING id", (image_id, timestamp, width, height, total_count, avg_conf) ) log_id = cur.fetchone()[0] # 步骤4:批量插入明细(关键性能优化点) detection_records = [] for det in json_data["detections"]: # 将归一化bbox转为像素坐标 x_min, y_min, x_max, y_max = det["bbox"] px_bbox = [ round(x_min * width, 3), round(y_min * height, 3), round(x_max * width, 3), round(y_max * height, 3) ] detection_records.append(( log_id, det["category"], det["confidence"], *px_bbox, det.get("attributes", {}).get("occluded", False), det.get("attributes", {}).get("truncated", False) )) # 一次批量插入,比循环insert快10倍+ execute_batch( cur, "INSERT INTO eagleeye_detections " "(log_id, category, confidence, x_min, y_min, x_max, y_max, is_occluded, is_truncated) " "VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)", detection_records ) db_conn.commit() return {"status": "success", "log_id": log_id, "detected_count": total_count} except Exception as e: db_conn.rollback() raise RuntimeError(f"DB write failed for {image_id}: {str(e)}") # 使用示例 if __name__ == "__main__": # 假设你已从EagleEye API拿到响应 with open("eagleeye_sample.json") as f: sample_json = json.load(f) conn = psycopg2.connect("host=localhost dbname=eagleeye user=ai password=secret") result = parse_and_store_detection_result(sample_json, conn) print(f"Stored successfully: {result}")脚本优势:
- 异常兜底:任何环节失败自动回滚,绝不留半截数据;
- 批量写入:
execute_batch替代循环execute,万级目标插入耗时<200ms; - 像素坐标转换:自动将
bbox归一化值乘以原图宽高,存为精确像素值; - 零外部依赖:仅需
psycopg2,无ORM,轻量可控。
3.3 查询实战:5个高频业务SQL
入库后,数据就活了。以下是运维、BI、算法同学最常写的SQL,直接复制可用:
-- Q1:过去24小时,各区域(按image_id前缀)未戴安全帽(hard_hat缺失)的人员数量 SELECT SUBSTRING(image_id FROM 1 FOR 3) AS region, COUNT(*) AS unsafe_person_count FROM eagleeye_detections d JOIN eagleeye_detection_logs l ON d.log_id = l.id WHERE l.detected_at > NOW() - INTERVAL '24 hours' AND d.category = 'person' AND d.confidence > 0.7 AND NOT EXISTS ( SELECT 1 FROM eagleeye_detections d2 WHERE d2.log_id = d.log_id AND d2.category = 'hard_hat' ) GROUP BY region; -- Q2:找出置信度最高、但面积最小的10个检测(可能是误报线索) SELECT category, confidence, area_px, x_min, y_min FROM eagleeye_detections WHERE confidence > 0.9 AND area_px < 500 ORDER BY confidence DESC LIMIT 10; -- Q3:统计每日检测总量趋势(供容量规划) SELECT DATE(detected_at) AS day, COUNT(*) AS total_detections, AVG(avg_confidence) AS avg_daily_conf FROM eagleeye_detection_logs GROUP BY DATE(detected_at) ORDER BY day DESC LIMIT 30;4. 常见陷阱与避坑指南
再好的方案,踩坑也会事倍功半。这些是我们在12个客户现场踩过的真坑:
4.1 时间戳陷阱:UTC vs 本地时间
EagleEye返回的timestamp是UTC时间(末尾带Z)。若你的数据库时区设为Asia/Shanghai,直接INSERT会导致时间快8小时。
正确做法:
- Python中用
datetime.fromisoformat(...).replace(tzinfo=timezone.utc)确保时区明确; - PostgreSQL中用
timestamptz类型存储,查询时用AT TIME ZONE 'Asia/Shanghai'转换。
4.2 bbox坐标陷阱:归一化≠像素,且顺序固定
新手常犯错误:
❌x_min = bbox[0] * 1920—— 错!bbox是[x_min, y_min, x_max, y_max],不是[x, y, w, h];
❌x_center = (bbox[0] + bbox[2]) / 2—— 对,但若没乘宽高,仍是无意义小数。
记住口诀:“归一化四元组,宽高相乘得像素,顺序永远是左上右下”。
4.3 类别一致性陷阱:模型升级导致category变更
TinyNAS模型迭代时,category名可能从"hard_hat"改为"safety_helmet"。若代码硬编码判断,一夜之间所有告警失效。
解决方案:
- 建立
category_mapping配置表,存model_version、raw_category、canonical_name; - 解析时先查映射表,再存
canonical_name; - 新模型上线前,先更新映射表,平滑过渡。
4.4 性能陷阱:单次请求处理1000+目标
当检测高密度场景(如地铁闸机人流),单图可能返回2000+目标。此时:
❌ 用json.loads()后遍历detections——内存暴涨;
改用ijson库流式解析:
import ijson parser = ijson.parse(open("huge.json", "rb")) # 逐个yield detections,内存占用恒定5. 总结:让EagleEye的数据真正流动起来
EagleEye的强大,不止于20ms的推理速度,更在于它输出的是一份可编程、可追溯、可分析的数据资产。本文带你走完了从“看到结果”到“用好数据”的关键闭环:
- 看清结构:明确了JSON中每个字段的真实含义、业务价值和使用前提,不再对着文档猜;
- 建好管道:给出了生产级数据库表设计和健壮入库脚本,数据进来就干净、可查、可扩展;
- 查出价值:提供了5个即查即用的SQL模板,覆盖统计、告警、分析核心场景;
- 避开深坑:总结了时间、坐标、类别、性能四大高频陷阱,省下你调试三天的时间。
下一步,你可以:
🔹 把eagleeye_detections表接入Grafana,实时看产线安全帽佩戴率;
🔹 用image_id关联OSS存储路径,点击数据库记录直接跳转查看原图;
🔹 将detections数据喂给时序数据库,训练预测模型(如“未来10分钟人员密度上升”)。
数据不会自己说话,但当你把它结构化、标准化、管道化之后,它就会成为你业务决策最冷静的参谋。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。