diff --git a/samples/built-in/detection/PFLD/README.md b/samples/built-in/detection/PFLD/README.md
index bf2ed44e504ce2caeed1420938eade2552e15656..69486e3eac7a0b338c483a3b97abcbfea16db522 100644
--- a/samples/built-in/detection/PFLD/README.md
+++ b/samples/built-in/detection/PFLD/README.md
@@ -171,7 +171,7 @@ mkdir -p model
## 安装依赖
```bash
-# 建议使用python3.7 or 3.8
+# 建议使用python3.7
pip3 install -r requirements.txt
```
diff --git a/samples/built-in/detection/PFLD/script/pfld_prepross.py b/samples/built-in/detection/PFLD/script/pfld_preprocess.py
similarity index 100%
rename from samples/built-in/detection/PFLD/script/pfld_prepross.py
rename to samples/built-in/detection/PFLD/script/pfld_preprocess.py
diff --git a/samples/built-in/detection/yolov4/script/yolo4_evaluate.py b/samples/built-in/detection/yolov4/script/yolo4_evaluate.py
index 9884f8b8e26514bb365e97739057670db9d76feb..848fd09c4d22515dcca489b5bde943908b625fd3 100644
--- a/samples/built-in/detection/yolov4/script/yolo4_evaluate.py
+++ b/samples/built-in/detection/yolov4/script/yolo4_evaluate.py
@@ -18,11 +18,41 @@ from pycocotools.cocoeval import COCOeval
import argparse
+def _build_image_name_map(coco_gt, all_img_ids):
+ """一次性建立 文件名stem -> image_id 映射,避免逐文件全量扫描 COCO images。"""
+ stem_to_img_id = {}
+ for info in coco_gt.loadImgs(all_img_ids):
+ stem = os.path.splitext(info["file_name"])[0]
+ # 若 stem 重复,保留首次出现的 image_id
+ stem_to_img_id.setdefault(stem, info["id"])
+ return stem_to_img_id
+
+
+def _resolve_image_id(img_name, all_img_id_set, stem_to_img_id):
+ """
+ 解析结果文件对应的 image_id,查不到时返回 None,交由调用方跳过。
+
+ 注意:不能写成
+ coco_gt.getImgIds(imgIds=[int(img_name)] if img_name.isdigit() else [])
+ 因为 pycocotools 的 getImgIds 在 imgIds 与 catIds 均为空时会返回全部图片 id,
+ 导致非数字命名的文件被静默误判为 img_ids[0](某张无关图片),
+ 把假阳性注入评估且不产生任何告警。
+ """
+ if img_name.isdigit():
+ numeric_id = int(img_name)
+ if numeric_id in all_img_id_set:
+ return numeric_id
+ return stem_to_img_id.get(img_name)
+
+
def run_evaluation(args):
"""读取txt检测结果并执行COCO评估"""
# 加载COCO标注
coco_gt = COCO(args.gt_annotations)
all_img_ids = coco_gt.getImgIds()
+ # 预建映射,将每个结果文件的 image_id 查找由 O(N) 降为 O(1)
+ all_img_id_set = set(all_img_ids)
+ stem_to_img_id = _build_image_name_map(coco_gt, all_img_ids)
# 创建结果列表
detection_results = []
@@ -39,19 +69,11 @@ def run_evaluation(args):
# 提取图片名称
img_name = os.path.splitext(txt_file)[0]
- # 查找图片ID
- img_ids = coco_gt.getImgIds(imgIds=[int(img_name)] if img_name.isdigit() else [])
- if not img_ids:
- # 尝试通过文件名查找
- img_infos = coco_gt.loadImgs(all_img_ids)
- matched = [info for info in img_infos if os.path.splitext(info['file_name'])[0] == img_name]
- if matched:
- img_id = matched[0]['id']
- else:
- print(f"警告: 未找到图片 {img_name} 对应的ID,跳过")
- continue
- else:
- img_id = img_ids[0]
+ # 查找图片ID;GT 中不存在的图片直接跳过,避免被误判为其他图片
+ img_id = _resolve_image_id(img_name, all_img_id_set, stem_to_img_id)
+ if img_id is None:
+ print(f"警告: 未找到图片 {img_name} 对应的ID,跳过")
+ continue
# 读取txt文件内容
txt_path = os.path.join(args.result_dir, txt_file)
diff --git a/samples/built-in/recognition/FaceNet/README.md b/samples/built-in/recognition/FaceNet/README.md
index c13f9500543e2c39ad68383f132e61b147d74951..77e61281950ada69ebdf7a93ad17bd23bf1be4d0 100644
--- a/samples/built-in/recognition/FaceNet/README.md
+++ b/samples/built-in/recognition/FaceNet/README.md
@@ -183,6 +183,14 @@ pip3 install -r requirements.txt
LFW(Labeled Faces in the Wild)是一个经典的无约束人脸识别基准数据集,包含 5749 个不同身份的 13233 张真实场景人脸图像,涵盖姿态、光照、表情等自然变异,适用于评估人脸识别算法在非受控环境下的泛化性能与匹配精度。
点击 [LFW](http://vis-www.cs.umass.edu/lfw/lfw.tgz) 下载数据集,点击 [pairs.txt](http://vis-www.cs.umass.edu/lfw/pairs.txt) 下载pairs.txt进行精度评估。
+
+ 若以上地址无法访问,可通过以下备用方式下载:
+
+ ```bash
+ wget -O lfw.tgz https://ndownloader.figshare.com/files/5976018
+
+ wget -O pairs.txt https://ndownloader.figshare.com/files/5976006
+ ```
在`modelzoo/datasets`源码目录下创建`LFW`文件夹,文件结构如下:
```
diff --git a/samples/samples_GPL/built-in/yolo11s-pose/README.md b/samples/samples_GPL/built-in/yolo11s-pose/README.md
index 4723dfc9bcbb285623dc38afd5a9f39380028fd2..d17191442595c3bde34c3f136351520ca7d3c33c 100644
--- a/samples/samples_GPL/built-in/yolo11s-pose/README.md
+++ b/samples/samples_GPL/built-in/yolo11s-pose/README.md
@@ -158,7 +158,7 @@ mkdir -p model
4. 切换到可执行文件main所在的目录,运行可执行文件。
```
- ./main --acl ../src/acl.json --model ../model/yolov11s-pose.om --input ../data/file_list_1.json
+ ./main --acl ../src/acl.json --model ../model/yolo11s-pose.om --input ../data/file_list_1.json
```
备注:若需要在数据集上进行精度评估,需要参考[安装依赖](#section183221994410)、[准备数据集](#section183221994411)和[精度&性能评估](#section741711594518)章节。
@@ -359,7 +359,7 @@ pip3 install -r requirements.txt
4. 验证batch_size的om模型的性能,参考命令如下:
```bash
- ./main --acl ../src/acl.json --model ../model/yolov11s-pose.om --input ../data/file_list_1.json
+ ./main --acl ../src/acl.json --model ../model/yolo11s-pose.om --input ../data/file_list_1.json
```
参数说明:(此模式下,输入路径为一张图片)
@@ -405,7 +405,7 @@ pip3 install -r requirements.txt
--preprocess_bin_dir "../data/preprocess/bin" \
--infer_bin_dir "../out/result_pc/bin" \
--file_list_path "../data/preprocess/file_list.txt" \
- --onnx_model_path "../model/yolov11s-pose.onnx" \
+ --onnx_model_path "../model/yolo11s-pose.onnx" \
--input_size 640 640
```
diff --git a/samples/samples_GPL/built-in/yolo11s-pose/requirements.txt b/samples/samples_GPL/built-in/yolo11s-pose/requirements.txt
index 90363012630475d60d2f26b4adbd0c071235088e..0bbbf70c8e1f149409be381437cd352b41527f4c 100644
--- a/samples/samples_GPL/built-in/yolo11s-pose/requirements.txt
+++ b/samples/samples_GPL/built-in/yolo11s-pose/requirements.txt
@@ -13,4 +13,5 @@ psutil
py-cpuinfo
pandas>=1.1.4
ultralytics-thop>=2.0.0
-ultralytics==8.3.232
\ No newline at end of file
+ultralytics==8.3.232
+pycocotools>=2.0.0
\ No newline at end of file
diff --git a/samples/samples_GPL/built-in/yolo11s-pose/script/yolo11s_pose_evaluate.py b/samples/samples_GPL/built-in/yolo11s-pose/script/yolo11s_pose_evaluate.py
index fdc1226db0f6c4113df11d599fa5e1f0432eb3df..27ea8de1bed10fb7dd5f468f1ba5007491313829 100644
--- a/samples/samples_GPL/built-in/yolo11s-pose/script/yolo11s_pose_evaluate.py
+++ b/samples/samples_GPL/built-in/yolo11s-pose/script/yolo11s_pose_evaluate.py
@@ -19,6 +19,33 @@ from pycocotools.cocoeval import COCOeval
import argparse
+def _build_image_name_map(coco_gt, all_img_ids):
+ """一次性建立 文件名stem -> image_id 映射,避免逐文件全量扫描 COCO images。"""
+ stem_to_img_id = {}
+ for info in coco_gt.loadImgs(all_img_ids):
+ stem = os.path.splitext(info["file_name"])[0]
+ # 若 stem 重复,保留首次出现的 image_id
+ stem_to_img_id.setdefault(stem, info["id"])
+ return stem_to_img_id
+
+
+def _resolve_image_id(img_name, all_img_id_set, stem_to_img_id):
+ """
+ 解析结果文件对应的 image_id,查不到时返回 None,交由调用方跳过。
+
+ 注意:不能写成
+ coco_gt.getImgIds(imgIds=[int(img_name)] if img_name.isdigit() else [])
+ 因为 pycocotools 的 getImgIds 在 imgIds 与 catIds 均为空时会返回全部图片 id,
+ 导致非数字命名的文件被静默误判为 img_ids[0](某张无关图片),
+ 把假阳性注入评估且不产生任何告警。
+ """
+ if img_name.isdigit():
+ numeric_id = int(img_name)
+ if numeric_id in all_img_id_set:
+ return numeric_id
+ return stem_to_img_id.get(img_name)
+
+
def run_evaluation(args):
"""执行COCO关键点评估"""
# 获取所有txt结果文件
@@ -30,6 +57,9 @@ def run_evaluation(args):
# 加载COCO标注
coco_gt = COCO(args.gt_annotations)
all_img_ids = coco_gt.getImgIds()
+ # 预建映射,将每个结果文件的 image_id 查找由 O(N) 降为 O(1)
+ all_img_id_set = set(all_img_ids)
+ stem_to_img_id = _build_image_name_map(coco_gt, all_img_ids)
# 创建结果列表
detection_results = []
@@ -40,19 +70,11 @@ def run_evaluation(args):
# 提取图片名称
img_name = os.path.splitext(txt_file)[0]
- # 查找图片ID
- img_ids = coco_gt.getImgIds(imgIds=[int(img_name)] if img_name.isdigit() else [])
- if not img_ids:
- # 尝试通过文件名查找
- img_infos = coco_gt.loadImgs(all_img_ids)
- matched = [info for info in img_infos if os.path.splitext(info['file_name'])[0] == img_name]
- if matched:
- img_id = matched[0]['id']
- else:
- print(f"警告: 未找到图片 {img_name} 对应的ID,跳过")
- continue
- else:
- img_id = img_ids[0]
+ # 查找图片ID;GT 中不存在的图片直接跳过,避免被误判为其他图片
+ img_id = _resolve_image_id(img_name, all_img_id_set, stem_to_img_id)
+ if img_id is None:
+ print(f"警告: 未找到图片 {img_name} 对应的ID,跳过")
+ continue
# 读取txt文件内容
txt_path = os.path.join(args.result_dir, txt_file)
diff --git a/samples/samples_GPL/built-in/yolo11s-seg/README.md b/samples/samples_GPL/built-in/yolo11s-seg/README.md
index 5ffab31f530f6295808ea2a7e88e7065079b63e5..3860ead8ac4be7ddcaff60f0007be19c3bf107ae 100644
--- a/samples/samples_GPL/built-in/yolo11s-seg/README.md
+++ b/samples/samples_GPL/built-in/yolo11s-seg/README.md
@@ -164,7 +164,7 @@ mkdir -p model
4. 切换到可执行文件main所在的目录,运行可执行文件。
```
- ./main --acl ../src/acl.json --model ../model/yolo11s_seg.om --input ../data/file_list_1.json
+ ./main --acl ../src/acl.json --model ../model/yolo11s-seg.om --input ../data/file_list_1.json
```
备注:若需要在数据集上进行精度评估,需要参考[安装依赖](#section183221994410)、[准备数据集](#section183221994411)和[精度&性能评估](#section741711594518)章节。
@@ -308,7 +308,7 @@ pip3 install -r requirements.txt
```bash
# 注意:实测模型推理速度较快,但是后处理时掩码解码部分较慢(为了评估精度,置信度候选框设置为0.001,保留了几乎所有候选框,导致端到端看起来比较慢);
# 在工业界使用时,置信度一般设置较高(如0.25),会过滤大量的无效框,后处理速度会快很多。另外后处理掩码矩阵计算部分,还可以使用NPU加速。
- ./main --acl ../src/acl.json --model ../model/yolo11s_seg.om --input ../data/file_list.json
+ ./main --acl ../src/acl.json --model ../model/yolo11s-seg.om --input ../data/file_list.json
```
推理结果会保存在result目录下
@@ -399,7 +399,7 @@ pip3 install -r requirements.txt
```bash
cd out
# 注意:性能测试不含前后处理逻辑,只有模型推理部分的耗时和FPS。因为工业界使用时前后处理可以通过CPU异步并行来加速或者会包含其他业务逻辑。
- ./main --acl ../src/acl.json --model ../model/yolo11s_seg.om --input ../data/file_list_1.json
+ ./main --acl ../src/acl.json --model ../model/yolo11s-seg.om --input ../data/file_list_1.json
cd ..
```
@@ -468,7 +468,7 @@ pip3 install -r requirements.txt
--bin_dir "../out/result_pc/bin" \
--img_dir "../../../../../datasets/coco2017/val2017" \
--output_dir "../out/result_pc/json" \
- --nms_threshold 0.7 \
+ --iou_threshold 0.7 \
--conf_threshold 0.001 \
--target_size 640 640
```
diff --git a/samples/samples_GPL/built-in/yolo11s-seg/script/yolo11s_seg_evaluate.py b/samples/samples_GPL/built-in/yolo11s-seg/script/yolo11s_seg_evaluate.py
index 378490596c4eaaa6703e0abfdcef94aecc914e3a..eae4c89445f3d25da8363f89f95c8b759e53d121 100644
--- a/samples/samples_GPL/built-in/yolo11s-seg/script/yolo11s_seg_evaluate.py
+++ b/samples/samples_GPL/built-in/yolo11s-seg/script/yolo11s_seg_evaluate.py
@@ -13,104 +13,401 @@
# limitations under the License.
import os
+import gc
+import copy
import json
import argparse
+from contextlib import redirect_stdout
+from io import StringIO
+
from pycocotools.coco import COCO
from pycocotools.cocoeval import COCOeval
from tqdm import tqdm
+# 仅作为内部实现参数,不改变脚本原有命令行接口。
+# 500 张/块在 COCO val2017 这类规模上通常能显著降低峰值内存。
+EVAL_CHUNK_SIZE = 500
+
+
+def _build_image_name_map(coco_gt, all_img_ids):
+ """一次性建立文件名 stem -> image_id 映射,避免逐文件全量扫描 COCO images。"""
+ stem_to_img_id = {}
+ for info in coco_gt.loadImgs(all_img_ids):
+ stem = os.path.splitext(info["file_name"])[0]
+ # 保持原脚本 matched[0] 的语义:若 stem 重复,保留首次出现的 image_id。
+ stem_to_img_id.setdefault(stem, info["id"])
+ return stem_to_img_id
+
+
+def _resolve_image_id(img_name, all_img_id_set, stem_to_img_id):
+ """保持原脚本的 image_id 查找语义,同时将非数字文件名查找从 O(N) 降为 O(1)。"""
+ if img_name.isdigit():
+ numeric_id = int(img_name)
+ if numeric_id in all_img_id_set:
+ return numeric_id
+ return stem_to_img_id.get(img_name)
+
+
+def _stream_merge_results(coco_gt, json_files, args):
+ """
+ 流式生成 merged_coco_results.json。
+
+ 不再构造全量 detection_results,仅在内存中保留单个 JSON 文件的结果。
+ 同时返回后续分块评估所需的小型 manifest:
+ [(image_id, [json_path1, json_path2, ...]), ...]
+ 仅包含“解析成功且至少有 1 条检测结果”的图片,与原脚本
+ set(res["image_id"] for res in detection_results) 的语义一致。
+
+ 同一个 image_id 若意外对应多个结果 JSON,会被归到同一个逻辑图片中,
+ 确保分块时不会把同一图片的 detections 拆到不同 chunk。
+ """
+ all_img_ids = coco_gt.getImgIds()
+ all_img_id_set = set(all_img_ids)
+ stem_to_img_id = _build_image_name_map(coco_gt, all_img_ids)
+
+ temp_json = os.path.join(args.result_dir, "../merged_coco_results.json")
+ eval_sources = {}
+ has_detection = False
+ first_result = True
+
+ with open(temp_json, "w") as merged_file:
+ merged_file.write("[")
+
+ for json_file in tqdm(json_files, desc="加载结果文件"):
+ img_name = os.path.splitext(json_file)[0]
+ img_id = _resolve_image_id(
+ img_name,
+ all_img_id_set,
+ stem_to_img_id,
+ )
+
+ if img_id is None:
+ print(f"警告: 未找到图片 {img_name} 对应的ID,跳过")
+ continue
+
+ json_path = os.path.join(args.result_dir, json_file)
+ file_has_detection = False
+ try:
+ with open(json_path, "r") as f:
+ results = json.load(f)
+
+ # 保持单图 JSON 中结果顺序不变,并逐条写入 merged 文件,
+ # 避免全量 detection_results 常驻内存。
+ for res in results:
+ res["image_id"] = img_id
+ if not first_result:
+ merged_file.write(", ")
+ json.dump(res, merged_file)
+ first_result = False
+ file_has_detection = True
+
+ except Exception as e:
+ print(f"警告: 处理文件 {json_file} 失败: {e},跳过")
+
+ # 原脚本即使单个文件处理中途异常,异常前已 append 的结果仍会保留。
+ # 这里同样只要成功处理过至少一条结果,就纳入评估 manifest。
+ if file_has_detection:
+ eval_sources.setdefault(img_id, []).append(json_path)
+ has_detection = True
+
+ merged_file.write("]")
+
+ print(f"合并的评估文件已保存至: {temp_json}")
+ eval_manifest = list(eval_sources.items())
+ return temp_json, eval_manifest, has_detection
+
+
+def _prepare_global_evaluator(coco_gt, img_ids, iou_type):
+ """
+ 创建用于最终 accumulate/summarize 的 COCOeval 容器。
+
+ evaluate() 的 evalImgs 展平顺序为:
+ category -> area range -> image
+ 因此预分配全局 evalImgs,分块 evaluate 后按全局索引回填。
+ """
+ coco_eval = COCOeval(coco_gt, None, iouType=iou_type)
+
+ # 模拟 COCOeval.evaluate() 开头对参数做的规范化。
+ coco_eval.params.imgIds = sorted(set(img_ids))
+ if coco_eval.params.useCats:
+ coco_eval.params.catIds = sorted(set(coco_eval.params.catIds))
+ coco_eval.params.maxDets = sorted(coco_eval.params.maxDets)
+
+ coco_eval._paramsEval = copy.deepcopy(coco_eval.params)
+
+ cat_ids = (
+ coco_eval.params.catIds
+ if coco_eval.params.useCats
+ else [-1]
+ )
+ num_categories = len(cat_ids)
+ num_areas = len(coco_eval.params.areaRng)
+ num_images = len(coco_eval.params.imgIds)
+
+ coco_eval.evalImgs = [None] * (
+ num_categories * num_areas * num_images
+ )
+
+ index_info = {
+ "img_pos": {
+ img_id: idx
+ for idx, img_id in enumerate(coco_eval.params.imgIds)
+ },
+ "cat_pos": {
+ cat_id: idx
+ for idx, cat_id in enumerate(cat_ids)
+ },
+ "area_pos": {
+ tuple(area_rng): idx
+ for idx, area_rng in enumerate(coco_eval.params.areaRng)
+ },
+ "num_images": num_images,
+ "num_areas": num_areas,
+ }
+ return coco_eval, index_info
+
+
+def _merge_chunk_eval_imgs(global_eval, global_index, chunk_eval):
+ """
+ 将单个 chunk 的 evalImgs 回填到完整 COCOeval 的全局 K×A×I 顺序。
+
+ 不能简单 global_eval.evalImgs.extend(chunk_eval.evalImgs),否则多个
+ chunk 拼接后会破坏 COCOeval.accumulate() 期待的展平顺序。
+ """
+ params = chunk_eval._paramsEval
+ chunk_img_ids = params.imgIds
+ chunk_cat_ids = params.catIds if params.useCats else [-1]
+
+ local_num_images = len(chunk_img_ids)
+ local_num_areas = len(params.areaRng)
+
+ global_num_images = global_index["num_images"]
+ global_num_areas = global_index["num_areas"]
+
+ for local_cat_idx, cat_id in enumerate(chunk_cat_ids):
+ global_cat_idx = global_index["cat_pos"][cat_id]
+
+ for local_area_idx, area_rng in enumerate(params.areaRng):
+ global_area_idx = global_index["area_pos"][tuple(area_rng)]
+
+ local_base = (
+ local_cat_idx * local_num_areas + local_area_idx
+ ) * local_num_images
+ global_base = (
+ global_cat_idx * global_num_areas + global_area_idx
+ ) * global_num_images
+
+ for local_img_idx, img_id in enumerate(chunk_img_ids):
+ global_img_idx = global_index["img_pos"][img_id]
+ global_eval.evalImgs[
+ global_base + global_img_idx
+ ] = chunk_eval.evalImgs[
+ local_base + local_img_idx
+ ]
+
+
+def _load_chunk_results(manifest_chunk):
+ """重新读取一个 chunk 的单图结果,并补齐正确 image_id。"""
+ chunk_results = []
+ chunk_img_ids = []
+
+ for img_id, json_paths in manifest_chunk:
+ image_has_results = False
+
+ # 同一 image_id 的多个 JSON 必须在同一个 chunk 内合并,
+ # 且保持它们在第一遍 os.listdir 遍历中的相对顺序。
+ for json_path in json_paths:
+ try:
+ with open(json_path, "r") as f:
+ results = json.load(f)
+
+ if not results:
+ continue
+
+ for res in results:
+ res["image_id"] = img_id
+ chunk_results.append(res)
+ image_has_results = True
+
+ except Exception as e:
+ print(
+ f"警告: 评估时重新读取文件 "
+ f"{os.path.basename(json_path)} 失败: {e},跳过"
+ )
+
+ if image_has_results:
+ chunk_img_ids.append(img_id)
+
+ return chunk_results, chunk_img_ids
+
+
+def _evaluate_in_chunks(coco_gt, eval_manifest):
+ """
+ 分块执行 bbox + segm 的 evaluate(),最后分别只 accumulate() 一次。
+
+ 每块只保留:
+ - 当前 chunk 的 detection Python 对象
+ - 当前 chunk 的 coco_dt / IoU
+ - 全局 evalImgs
+
+ 从而避免 loadRes(完整 merged JSON) 带来的数 GB Python 对象常驻内存。
+ """
+ evaluated_img_ids = sorted({
+ img_id
+ for img_id, _ in eval_manifest
+ })
+
+ bbox_eval, bbox_index = _prepare_global_evaluator(
+ coco_gt,
+ evaluated_img_ids,
+ "bbox",
+ )
+ segm_eval, segm_index = _prepare_global_evaluator(
+ coco_gt,
+ evaluated_img_ids,
+ "segm",
+ )
+
+ total_chunks = (
+ len(eval_manifest) + EVAL_CHUNK_SIZE - 1
+ ) // EVAL_CHUNK_SIZE
+
+ for start in tqdm(
+ range(0, len(eval_manifest), EVAL_CHUNK_SIZE),
+ total=total_chunks,
+ desc="分块COCO评估",
+ ):
+ manifest_chunk = eval_manifest[
+ start:start + EVAL_CHUNK_SIZE
+ ]
+ chunk_results, chunk_img_ids = _load_chunk_results(
+ manifest_chunk
+ )
+
+ if not chunk_results:
+ continue
+
+ # loadRes 支持直接传 Python list,不需要为每个 chunk 再落临时 JSON。
+ coco_dt = coco_gt.loadRes(chunk_results)
+
+ # bbox evaluate
+ chunk_bbox = COCOeval(
+ coco_gt,
+ coco_dt,
+ iouType="bbox",
+ )
+ chunk_bbox.params.imgIds = chunk_img_ids
+
+ # COCOeval.evaluate() 会打印固定日志;分块执行会重复很多次。
+ # 这里静默 chunk 内部日志,最终 accumulate/summarize 输出保持清晰。
+ with redirect_stdout(StringIO()):
+ chunk_bbox.evaluate()
+
+ _merge_chunk_eval_imgs(
+ bbox_eval,
+ bbox_index,
+ chunk_bbox,
+ )
+
+ # 尽早释放 bbox chunk 的 IoU 和 evaluator。
+ chunk_bbox.ious = {}
+ chunk_bbox.evalImgs = []
+ del chunk_bbox
+ gc.collect()
+
+ # segm evaluate
+ chunk_segm = COCOeval(
+ coco_gt,
+ coco_dt,
+ iouType="segm",
+ )
+ chunk_segm.params.imgIds = chunk_img_ids
+
+ with redirect_stdout(StringIO()):
+ chunk_segm.evaluate()
+
+ _merge_chunk_eval_imgs(
+ segm_eval,
+ segm_index,
+ chunk_segm,
+ )
+
+ chunk_segm.ious = {}
+ chunk_segm.evalImgs = []
+ del chunk_segm
+
+ # chunk_results 与 coco_dt 引用同一批 annotation dict,
+ # 当前块完成后一起释放。
+ del coco_dt
+ del chunk_results
+ gc.collect()
+
+ return bbox_eval, segm_eval
+
+
def run_evaluation(args):
"""读取单张图片的JSON结果并执行COCO评估"""
# 加载COCO标注
coco_gt = COCO(args.gt_annotations)
- all_img_ids = coco_gt.getImgIds()
-
- # 创建结果列表
- detection_results = []
# 获取所有JSON结果文件
- json_files = [f for f in os.listdir(args.result_dir) if f.endswith('.json')]
+ json_files = [
+ f
+ for f in os.listdir(args.result_dir)
+ if f.endswith(".json")
+ ]
if not json_files:
print(f"错误: 在 {args.result_dir} 中未找到任何JSON文件")
return
- # 处理每个JSON结果
- total = len(json_files)
- for idx, json_file in enumerate(tqdm(json_files, desc="加载结果文件")):
- # 提取图片名称
- img_name = os.path.splitext(json_file)[0]
-
- # 查找图片ID(确保与标注文件中的ID匹配)
- img_ids = coco_gt.getImgIds(imgIds=[int(img_name)] if img_name.isdigit() else [])
- if not img_ids:
- # 尝试通过文件名查找
- img_infos = coco_gt.loadImgs(all_img_ids)
- matched = [info for info in img_infos if os.path.splitext(info['file_name'])[0] == img_name]
- if matched:
- img_id = matched[0]['id']
- else:
- print(f"警告: 未找到图片 {img_name} 对应的ID,跳过")
- continue
- else:
- img_id = img_ids[0]
-
- # 读取JSON文件内容
- json_path = os.path.join(args.result_dir, json_file)
- try:
- with open(json_path, 'r') as f:
- results = json.load(f)
-
- # 确保结果中的image_id正确
- for res in results:
- res["image_id"] = img_id
- detection_results.append(res)
-
- except Exception as e:
- print(f"警告: 处理文件 {json_file} 失败: {e},跳过")
- continue
-
- # 保存合并的JSON结果(用于COCO评估)
- temp_json = os.path.join(args.result_dir, "../merged_coco_results.json")
- with open(temp_json, "w") as f:
- json.dump(detection_results, f)
- print(f"合并的评估文件已保存至: {temp_json}")
+ # 第一遍:
+ # 1. 流式生成 merged_coco_results.json;
+ # 2. 记录真正存在检测结果的图片,用于后续分块评估。
+ _, eval_manifest, has_detection = _stream_merge_results(
+ coco_gt,
+ json_files,
+ args,
+ )
- # 执行COCO评估
- if not detection_results:
+ if not has_detection:
print("警告: 没有有效的检测结果,无法进行评估")
return
- coco_dt = coco_gt.loadRes(temp_json)
+ # 第二遍:
+ # 按图片分块读取结果,每块分别执行 bbox / segm evaluate,
+ # 避免完整 merged JSON 经 loadRes/json.load 后常驻数 GB 内存。
+ coco_eval_bbox, coco_eval_segm = _evaluate_in_chunks(
+ coco_gt,
+ eval_manifest,
+ )
# -------------------------- 1. 评估框检测(bbox)--------------------------
print("=" * 50)
print("框检测(bbox)评估结果:")
- coco_eval_bbox = COCOeval(coco_gt, coco_dt, iouType='bbox')
- # 确保评估使用与推理相同的图片集
- coco_eval_bbox.params.imgIds = list(set(res["image_id"] for res in detection_results))
- coco_eval_bbox.evaluate()
coco_eval_bbox.accumulate()
coco_eval_bbox.summarize()
# -------------------------- 2. 评估分割(segm)--------------------------
print("\n" + "=" * 50)
print("分割(segm)评估结果:")
- coco_eval_segm = COCOeval(coco_gt, coco_dt, iouType='segm')
- coco_eval_segm.params.imgIds = list(set(res["image_id"] for res in detection_results))
- coco_eval_segm.evaluate()
coco_eval_segm.accumulate()
coco_eval_segm.summarize()
def main():
# 解析命令行参数
- parser = argparse.ArgumentParser(description='根据单张图片的JSON结果进行COCO评估')
- parser.add_argument('--result_dir', default='../out/result/json',
- help='存放单张图片JSON结果的目录')
- parser.add_argument('--gt_annotations',
- default="../../../../../datasets/coco2017/annotations/instances_val2017.json",
- help='COCO的ground truth标注文件路径')
+ parser = argparse.ArgumentParser(description="根据单张图片的JSON结果进行COCO评估")
+ parser.add_argument(
+ "--result_dir",
+ default="../out/result/json",
+ help="存放单张图片JSON结果的目录",
+ )
+ parser.add_argument(
+ "--gt_annotations",
+ default="../../../../../datasets/coco2017/annotations/instances_val2017.json",
+ help="COCO的ground truth标注文件路径",
+ )
args = parser.parse_args()
diff --git a/samples/samples_GPL/built-in/yolov6s/README.md b/samples/samples_GPL/built-in/yolov6s/README.md
index fd7c4dd3d71ef2d80a1aac4080f29bfd29f629ba..8f4ffe4a0e70cd6c55ee186e185f3d737219fec8 100644
--- a/samples/samples_GPL/built-in/yolov6s/README.md
+++ b/samples/samples_GPL/built-in/yolov6s/README.md
@@ -163,7 +163,7 @@ mkdir -p model
## 安装依赖
```bash
-# 建议使用 Python 3.7.5
+# 建议使用 Python 3.8
pip3 install -r requirements.txt
```
@@ -228,7 +228,10 @@ pip3 install -r requirements.txt
在 https://github.com/meituan/YOLOv6 中找到所需版本下载,也可以使用下述命令下载。
```
+ mkdir -p model
+ cd model
wget https://github.com/meituan/YOLOv6/releases/download/0.4.0/yolov6s.pt
+ cd ..
```
3. 导出onnx文件。
@@ -236,17 +239,15 @@ pip3 install -r requirements.txt
使用YOLOv6/deploy/ONNX/export_onnx.py导出onnx模型
```bash
- mkdir -p model
cd YOLOv6
python3 ./deploy/ONNX/export_onnx.py \
- --weights yolov6s.pt \
+ --weights ../model/yolov6s.pt \
--img 640 \
--batch 1 \
--simplify
cd ..
- mv YOLOv6/yolov6s.onnx ./model
```
4. 使用ATC工具将ONNX模型转OM模型。
diff --git a/samples/samples_GPL/built-in/yolov6s/script/yolo6s_evaluate.py b/samples/samples_GPL/built-in/yolov6s/script/yolo6s_evaluate.py
index ec49dcb6fce30378206e1b308e7acb1df7f664e8..5abc326e02da1ddfcf26baa81c3bd82af22289aa 100644
--- a/samples/samples_GPL/built-in/yolov6s/script/yolo6s_evaluate.py
+++ b/samples/samples_GPL/built-in/yolov6s/script/yolo6s_evaluate.py
@@ -19,11 +19,41 @@ from pycocotools.cocoeval import COCOeval
import argparse
+def _build_image_name_map(coco_gt, all_img_ids):
+ """一次性建立 文件名stem -> image_id 映射,避免逐文件全量扫描 COCO images。"""
+ stem_to_img_id = {}
+ for info in coco_gt.loadImgs(all_img_ids):
+ stem = os.path.splitext(info["file_name"])[0]
+ # 若 stem 重复,保留首次出现的 image_id
+ stem_to_img_id.setdefault(stem, info["id"])
+ return stem_to_img_id
+
+
+def _resolve_image_id(img_name, all_img_id_set, stem_to_img_id):
+ """
+ 解析结果文件对应的 image_id,查不到时返回 None,交由调用方跳过。
+
+ 注意:不能写成
+ coco_gt.getImgIds(imgIds=[int(img_name)] if img_name.isdigit() else [])
+ 因为 pycocotools 的 getImgIds 在 imgIds 与 catIds 均为空时会返回全部图片 id,
+ 导致非数字命名的文件被静默误判为 img_ids[0](某张无关图片),
+ 把假阳性注入评估且不产生任何告警。
+ """
+ if img_name.isdigit():
+ numeric_id = int(img_name)
+ if numeric_id in all_img_id_set:
+ return numeric_id
+ return stem_to_img_id.get(img_name)
+
+
def run_evaluation(args):
"""读取txt检测结果并执行COCO评估"""
# 加载COCO标注
coco_gt = COCO(args.gt_annotations)
all_img_ids = coco_gt.getImgIds()
+ # 预建映射,将每个结果文件的 image_id 查找由 O(N) 降为 O(1)
+ all_img_id_set = set(all_img_ids)
+ stem_to_img_id = _build_image_name_map(coco_gt, all_img_ids)
# 创建结果列表
detection_results = []
@@ -40,19 +70,11 @@ def run_evaluation(args):
# 提取图片名称
img_name = os.path.splitext(txt_file)[0]
- # 查找图片ID
- img_ids = coco_gt.getImgIds(imgIds=[int(img_name)] if img_name.isdigit() else [])
- if not img_ids:
- # 尝试通过文件名查找
- img_infos = coco_gt.loadImgs(all_img_ids)
- matched = [info for info in img_infos if os.path.splitext(info['file_name'])[0] == img_name]
- if matched:
- img_id = matched[0]['id']
- else:
- print(f"警告: 未找到图片 {img_name} 对应的ID,跳过")
- continue
- else:
- img_id = img_ids[0]
+ # 查找图片ID;GT 中不存在的图片直接跳过,避免被误判为其他图片
+ img_id = _resolve_image_id(img_name, all_img_id_set, stem_to_img_id)
+ if img_id is None:
+ print(f"警告: 未找到图片 {img_name} 对应的ID,跳过")
+ continue
# 读取txt文件内容
txt_path = os.path.join(args.result_dir, txt_file)
diff --git a/samples/samples_GPL/built-in/yolov8s-seg/README.md b/samples/samples_GPL/built-in/yolov8s-seg/README.md
index 519ee1dac86ab2d1ed29f7d963c0c8e8a3faf7ae..b5f38b5e37c09415ecef8f3ca1db6e1894645b1a 100644
--- a/samples/samples_GPL/built-in/yolov8s-seg/README.md
+++ b/samples/samples_GPL/built-in/yolov8s-seg/README.md
@@ -164,7 +164,7 @@ mkdir -p model
4. 切换到可执行文件main所在的目录,运行可执行文件。
```
- ./main --acl ../src/acl.json --model ../model/yolov8s_seg.om --input ../data/file_list_1.json
+ ./main --acl ../src/acl.json --model ../model/yolov8s-seg.om --input ../data/file_list_1.json
```
备注:若需要在数据集上进行精度评估,需要参考[安装依赖](#section183221994410)、[准备数据集](#section183221994411)和[精度&性能评估](#section741711594518)章节。
@@ -469,7 +469,7 @@ pip3 install -r requirements.txt
--bin_dir "../out/result_pc/bin" \
--img_dir "../../../../../datasets/coco2017/val2017" \
--output_dir "../out/result_pc/json" \
- --nms_threshold 0.7 \
+ --iou_threshold 0.7 \
--conf_threshold 0.001 \
--target_size 640 640
cd ..
diff --git a/samples/samples_GPL/built-in/yolov8s-seg/script/yolov8s_seg_evaluate.py b/samples/samples_GPL/built-in/yolov8s-seg/script/yolov8s_seg_evaluate.py
index 378490596c4eaaa6703e0abfdcef94aecc914e3a..eae4c89445f3d25da8363f89f95c8b759e53d121 100644
--- a/samples/samples_GPL/built-in/yolov8s-seg/script/yolov8s_seg_evaluate.py
+++ b/samples/samples_GPL/built-in/yolov8s-seg/script/yolov8s_seg_evaluate.py
@@ -13,104 +13,401 @@
# limitations under the License.
import os
+import gc
+import copy
import json
import argparse
+from contextlib import redirect_stdout
+from io import StringIO
+
from pycocotools.coco import COCO
from pycocotools.cocoeval import COCOeval
from tqdm import tqdm
+# 仅作为内部实现参数,不改变脚本原有命令行接口。
+# 500 张/块在 COCO val2017 这类规模上通常能显著降低峰值内存。
+EVAL_CHUNK_SIZE = 500
+
+
+def _build_image_name_map(coco_gt, all_img_ids):
+ """一次性建立文件名 stem -> image_id 映射,避免逐文件全量扫描 COCO images。"""
+ stem_to_img_id = {}
+ for info in coco_gt.loadImgs(all_img_ids):
+ stem = os.path.splitext(info["file_name"])[0]
+ # 保持原脚本 matched[0] 的语义:若 stem 重复,保留首次出现的 image_id。
+ stem_to_img_id.setdefault(stem, info["id"])
+ return stem_to_img_id
+
+
+def _resolve_image_id(img_name, all_img_id_set, stem_to_img_id):
+ """保持原脚本的 image_id 查找语义,同时将非数字文件名查找从 O(N) 降为 O(1)。"""
+ if img_name.isdigit():
+ numeric_id = int(img_name)
+ if numeric_id in all_img_id_set:
+ return numeric_id
+ return stem_to_img_id.get(img_name)
+
+
+def _stream_merge_results(coco_gt, json_files, args):
+ """
+ 流式生成 merged_coco_results.json。
+
+ 不再构造全量 detection_results,仅在内存中保留单个 JSON 文件的结果。
+ 同时返回后续分块评估所需的小型 manifest:
+ [(image_id, [json_path1, json_path2, ...]), ...]
+ 仅包含“解析成功且至少有 1 条检测结果”的图片,与原脚本
+ set(res["image_id"] for res in detection_results) 的语义一致。
+
+ 同一个 image_id 若意外对应多个结果 JSON,会被归到同一个逻辑图片中,
+ 确保分块时不会把同一图片的 detections 拆到不同 chunk。
+ """
+ all_img_ids = coco_gt.getImgIds()
+ all_img_id_set = set(all_img_ids)
+ stem_to_img_id = _build_image_name_map(coco_gt, all_img_ids)
+
+ temp_json = os.path.join(args.result_dir, "../merged_coco_results.json")
+ eval_sources = {}
+ has_detection = False
+ first_result = True
+
+ with open(temp_json, "w") as merged_file:
+ merged_file.write("[")
+
+ for json_file in tqdm(json_files, desc="加载结果文件"):
+ img_name = os.path.splitext(json_file)[0]
+ img_id = _resolve_image_id(
+ img_name,
+ all_img_id_set,
+ stem_to_img_id,
+ )
+
+ if img_id is None:
+ print(f"警告: 未找到图片 {img_name} 对应的ID,跳过")
+ continue
+
+ json_path = os.path.join(args.result_dir, json_file)
+ file_has_detection = False
+ try:
+ with open(json_path, "r") as f:
+ results = json.load(f)
+
+ # 保持单图 JSON 中结果顺序不变,并逐条写入 merged 文件,
+ # 避免全量 detection_results 常驻内存。
+ for res in results:
+ res["image_id"] = img_id
+ if not first_result:
+ merged_file.write(", ")
+ json.dump(res, merged_file)
+ first_result = False
+ file_has_detection = True
+
+ except Exception as e:
+ print(f"警告: 处理文件 {json_file} 失败: {e},跳过")
+
+ # 原脚本即使单个文件处理中途异常,异常前已 append 的结果仍会保留。
+ # 这里同样只要成功处理过至少一条结果,就纳入评估 manifest。
+ if file_has_detection:
+ eval_sources.setdefault(img_id, []).append(json_path)
+ has_detection = True
+
+ merged_file.write("]")
+
+ print(f"合并的评估文件已保存至: {temp_json}")
+ eval_manifest = list(eval_sources.items())
+ return temp_json, eval_manifest, has_detection
+
+
+def _prepare_global_evaluator(coco_gt, img_ids, iou_type):
+ """
+ 创建用于最终 accumulate/summarize 的 COCOeval 容器。
+
+ evaluate() 的 evalImgs 展平顺序为:
+ category -> area range -> image
+ 因此预分配全局 evalImgs,分块 evaluate 后按全局索引回填。
+ """
+ coco_eval = COCOeval(coco_gt, None, iouType=iou_type)
+
+ # 模拟 COCOeval.evaluate() 开头对参数做的规范化。
+ coco_eval.params.imgIds = sorted(set(img_ids))
+ if coco_eval.params.useCats:
+ coco_eval.params.catIds = sorted(set(coco_eval.params.catIds))
+ coco_eval.params.maxDets = sorted(coco_eval.params.maxDets)
+
+ coco_eval._paramsEval = copy.deepcopy(coco_eval.params)
+
+ cat_ids = (
+ coco_eval.params.catIds
+ if coco_eval.params.useCats
+ else [-1]
+ )
+ num_categories = len(cat_ids)
+ num_areas = len(coco_eval.params.areaRng)
+ num_images = len(coco_eval.params.imgIds)
+
+ coco_eval.evalImgs = [None] * (
+ num_categories * num_areas * num_images
+ )
+
+ index_info = {
+ "img_pos": {
+ img_id: idx
+ for idx, img_id in enumerate(coco_eval.params.imgIds)
+ },
+ "cat_pos": {
+ cat_id: idx
+ for idx, cat_id in enumerate(cat_ids)
+ },
+ "area_pos": {
+ tuple(area_rng): idx
+ for idx, area_rng in enumerate(coco_eval.params.areaRng)
+ },
+ "num_images": num_images,
+ "num_areas": num_areas,
+ }
+ return coco_eval, index_info
+
+
+def _merge_chunk_eval_imgs(global_eval, global_index, chunk_eval):
+ """
+ 将单个 chunk 的 evalImgs 回填到完整 COCOeval 的全局 K×A×I 顺序。
+
+ 不能简单 global_eval.evalImgs.extend(chunk_eval.evalImgs),否则多个
+ chunk 拼接后会破坏 COCOeval.accumulate() 期待的展平顺序。
+ """
+ params = chunk_eval._paramsEval
+ chunk_img_ids = params.imgIds
+ chunk_cat_ids = params.catIds if params.useCats else [-1]
+
+ local_num_images = len(chunk_img_ids)
+ local_num_areas = len(params.areaRng)
+
+ global_num_images = global_index["num_images"]
+ global_num_areas = global_index["num_areas"]
+
+ for local_cat_idx, cat_id in enumerate(chunk_cat_ids):
+ global_cat_idx = global_index["cat_pos"][cat_id]
+
+ for local_area_idx, area_rng in enumerate(params.areaRng):
+ global_area_idx = global_index["area_pos"][tuple(area_rng)]
+
+ local_base = (
+ local_cat_idx * local_num_areas + local_area_idx
+ ) * local_num_images
+ global_base = (
+ global_cat_idx * global_num_areas + global_area_idx
+ ) * global_num_images
+
+ for local_img_idx, img_id in enumerate(chunk_img_ids):
+ global_img_idx = global_index["img_pos"][img_id]
+ global_eval.evalImgs[
+ global_base + global_img_idx
+ ] = chunk_eval.evalImgs[
+ local_base + local_img_idx
+ ]
+
+
+def _load_chunk_results(manifest_chunk):
+ """重新读取一个 chunk 的单图结果,并补齐正确 image_id。"""
+ chunk_results = []
+ chunk_img_ids = []
+
+ for img_id, json_paths in manifest_chunk:
+ image_has_results = False
+
+ # 同一 image_id 的多个 JSON 必须在同一个 chunk 内合并,
+ # 且保持它们在第一遍 os.listdir 遍历中的相对顺序。
+ for json_path in json_paths:
+ try:
+ with open(json_path, "r") as f:
+ results = json.load(f)
+
+ if not results:
+ continue
+
+ for res in results:
+ res["image_id"] = img_id
+ chunk_results.append(res)
+ image_has_results = True
+
+ except Exception as e:
+ print(
+ f"警告: 评估时重新读取文件 "
+ f"{os.path.basename(json_path)} 失败: {e},跳过"
+ )
+
+ if image_has_results:
+ chunk_img_ids.append(img_id)
+
+ return chunk_results, chunk_img_ids
+
+
+def _evaluate_in_chunks(coco_gt, eval_manifest):
+ """
+ 分块执行 bbox + segm 的 evaluate(),最后分别只 accumulate() 一次。
+
+ 每块只保留:
+ - 当前 chunk 的 detection Python 对象
+ - 当前 chunk 的 coco_dt / IoU
+ - 全局 evalImgs
+
+ 从而避免 loadRes(完整 merged JSON) 带来的数 GB Python 对象常驻内存。
+ """
+ evaluated_img_ids = sorted({
+ img_id
+ for img_id, _ in eval_manifest
+ })
+
+ bbox_eval, bbox_index = _prepare_global_evaluator(
+ coco_gt,
+ evaluated_img_ids,
+ "bbox",
+ )
+ segm_eval, segm_index = _prepare_global_evaluator(
+ coco_gt,
+ evaluated_img_ids,
+ "segm",
+ )
+
+ total_chunks = (
+ len(eval_manifest) + EVAL_CHUNK_SIZE - 1
+ ) // EVAL_CHUNK_SIZE
+
+ for start in tqdm(
+ range(0, len(eval_manifest), EVAL_CHUNK_SIZE),
+ total=total_chunks,
+ desc="分块COCO评估",
+ ):
+ manifest_chunk = eval_manifest[
+ start:start + EVAL_CHUNK_SIZE
+ ]
+ chunk_results, chunk_img_ids = _load_chunk_results(
+ manifest_chunk
+ )
+
+ if not chunk_results:
+ continue
+
+ # loadRes 支持直接传 Python list,不需要为每个 chunk 再落临时 JSON。
+ coco_dt = coco_gt.loadRes(chunk_results)
+
+ # bbox evaluate
+ chunk_bbox = COCOeval(
+ coco_gt,
+ coco_dt,
+ iouType="bbox",
+ )
+ chunk_bbox.params.imgIds = chunk_img_ids
+
+ # COCOeval.evaluate() 会打印固定日志;分块执行会重复很多次。
+ # 这里静默 chunk 内部日志,最终 accumulate/summarize 输出保持清晰。
+ with redirect_stdout(StringIO()):
+ chunk_bbox.evaluate()
+
+ _merge_chunk_eval_imgs(
+ bbox_eval,
+ bbox_index,
+ chunk_bbox,
+ )
+
+ # 尽早释放 bbox chunk 的 IoU 和 evaluator。
+ chunk_bbox.ious = {}
+ chunk_bbox.evalImgs = []
+ del chunk_bbox
+ gc.collect()
+
+ # segm evaluate
+ chunk_segm = COCOeval(
+ coco_gt,
+ coco_dt,
+ iouType="segm",
+ )
+ chunk_segm.params.imgIds = chunk_img_ids
+
+ with redirect_stdout(StringIO()):
+ chunk_segm.evaluate()
+
+ _merge_chunk_eval_imgs(
+ segm_eval,
+ segm_index,
+ chunk_segm,
+ )
+
+ chunk_segm.ious = {}
+ chunk_segm.evalImgs = []
+ del chunk_segm
+
+ # chunk_results 与 coco_dt 引用同一批 annotation dict,
+ # 当前块完成后一起释放。
+ del coco_dt
+ del chunk_results
+ gc.collect()
+
+ return bbox_eval, segm_eval
+
+
def run_evaluation(args):
"""读取单张图片的JSON结果并执行COCO评估"""
# 加载COCO标注
coco_gt = COCO(args.gt_annotations)
- all_img_ids = coco_gt.getImgIds()
-
- # 创建结果列表
- detection_results = []
# 获取所有JSON结果文件
- json_files = [f for f in os.listdir(args.result_dir) if f.endswith('.json')]
+ json_files = [
+ f
+ for f in os.listdir(args.result_dir)
+ if f.endswith(".json")
+ ]
if not json_files:
print(f"错误: 在 {args.result_dir} 中未找到任何JSON文件")
return
- # 处理每个JSON结果
- total = len(json_files)
- for idx, json_file in enumerate(tqdm(json_files, desc="加载结果文件")):
- # 提取图片名称
- img_name = os.path.splitext(json_file)[0]
-
- # 查找图片ID(确保与标注文件中的ID匹配)
- img_ids = coco_gt.getImgIds(imgIds=[int(img_name)] if img_name.isdigit() else [])
- if not img_ids:
- # 尝试通过文件名查找
- img_infos = coco_gt.loadImgs(all_img_ids)
- matched = [info for info in img_infos if os.path.splitext(info['file_name'])[0] == img_name]
- if matched:
- img_id = matched[0]['id']
- else:
- print(f"警告: 未找到图片 {img_name} 对应的ID,跳过")
- continue
- else:
- img_id = img_ids[0]
-
- # 读取JSON文件内容
- json_path = os.path.join(args.result_dir, json_file)
- try:
- with open(json_path, 'r') as f:
- results = json.load(f)
-
- # 确保结果中的image_id正确
- for res in results:
- res["image_id"] = img_id
- detection_results.append(res)
-
- except Exception as e:
- print(f"警告: 处理文件 {json_file} 失败: {e},跳过")
- continue
-
- # 保存合并的JSON结果(用于COCO评估)
- temp_json = os.path.join(args.result_dir, "../merged_coco_results.json")
- with open(temp_json, "w") as f:
- json.dump(detection_results, f)
- print(f"合并的评估文件已保存至: {temp_json}")
+ # 第一遍:
+ # 1. 流式生成 merged_coco_results.json;
+ # 2. 记录真正存在检测结果的图片,用于后续分块评估。
+ _, eval_manifest, has_detection = _stream_merge_results(
+ coco_gt,
+ json_files,
+ args,
+ )
- # 执行COCO评估
- if not detection_results:
+ if not has_detection:
print("警告: 没有有效的检测结果,无法进行评估")
return
- coco_dt = coco_gt.loadRes(temp_json)
+ # 第二遍:
+ # 按图片分块读取结果,每块分别执行 bbox / segm evaluate,
+ # 避免完整 merged JSON 经 loadRes/json.load 后常驻数 GB 内存。
+ coco_eval_bbox, coco_eval_segm = _evaluate_in_chunks(
+ coco_gt,
+ eval_manifest,
+ )
# -------------------------- 1. 评估框检测(bbox)--------------------------
print("=" * 50)
print("框检测(bbox)评估结果:")
- coco_eval_bbox = COCOeval(coco_gt, coco_dt, iouType='bbox')
- # 确保评估使用与推理相同的图片集
- coco_eval_bbox.params.imgIds = list(set(res["image_id"] for res in detection_results))
- coco_eval_bbox.evaluate()
coco_eval_bbox.accumulate()
coco_eval_bbox.summarize()
# -------------------------- 2. 评估分割(segm)--------------------------
print("\n" + "=" * 50)
print("分割(segm)评估结果:")
- coco_eval_segm = COCOeval(coco_gt, coco_dt, iouType='segm')
- coco_eval_segm.params.imgIds = list(set(res["image_id"] for res in detection_results))
- coco_eval_segm.evaluate()
coco_eval_segm.accumulate()
coco_eval_segm.summarize()
def main():
# 解析命令行参数
- parser = argparse.ArgumentParser(description='根据单张图片的JSON结果进行COCO评估')
- parser.add_argument('--result_dir', default='../out/result/json',
- help='存放单张图片JSON结果的目录')
- parser.add_argument('--gt_annotations',
- default="../../../../../datasets/coco2017/annotations/instances_val2017.json",
- help='COCO的ground truth标注文件路径')
+ parser = argparse.ArgumentParser(description="根据单张图片的JSON结果进行COCO评估")
+ parser.add_argument(
+ "--result_dir",
+ default="../out/result/json",
+ help="存放单张图片JSON结果的目录",
+ )
+ parser.add_argument(
+ "--gt_annotations",
+ default="../../../../../datasets/coco2017/annotations/instances_val2017.json",
+ help="COCO的ground truth标注文件路径",
+ )
args = parser.parse_args()
diff --git a/samples/samples_GPL/built-in/yolov9s/data/file_list_1.json b/samples/samples_GPL/built-in/yolov9s/data/file_list_1.json
index 2c906e43413b85f7825aa5f2fa3b940395db43d4..2cdd4a4edae0e1a42a5c47cae2403d07fbfeef46 100644
--- a/samples/samples_GPL/built-in/yolov9s/data/file_list_1.json
+++ b/samples/samples_GPL/built-in/yolov9s/data/file_list_1.json
@@ -2,7 +2,7 @@
"loop": 300,
"fileList": [
[
- "../datasets/coco/images/val2017/000000000139.jpg"
+ "../../../../../datasets/testdata/2.jpg"
]
]
}
diff --git a/samples/samples_GPL/common/infer/post_process/yolo11s_seg_postprocess.cpp b/samples/samples_GPL/common/infer/post_process/yolo11s_seg_postprocess.cpp
index 460341925813bf7dae0ace54d57d168328318a8f..c4fcd6b857a271265ae12f44a2a9c2b9e5a45784 100644
--- a/samples/samples_GPL/common/infer/post_process/yolo11s_seg_postprocess.cpp
+++ b/samples/samples_GPL/common/infer/post_process/yolo11s_seg_postprocess.cpp
@@ -57,6 +57,17 @@ namespace Yolo11sSegNS {
float input_x1 = 0.0f, input_y1 = 0.0f, input_x2 = 0.0f, input_y2 = 0.0f;
};
+ struct SegmentationResult {
+ int imageId = 0;
+ int categoryId = -1;
+ float x = 0.0f;
+ float y = 0.0f;
+ float width = 0.0f;
+ float height = 0.0f;
+ float score = 0.0f;
+ vector> segmentation;
+ };
+
const vector Yolo80ToCoco90 = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 27, 28, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
@@ -243,12 +254,11 @@ namespace Yolo11sSegNS {
return validSegments;
}
- void SaveJsonResults(const vector& results, const string& savePath, const string& fileName,
+ vector BuildSegmentationResults(const vector& results, const string& fileName,
const float* protos, int targetWidth, int targetHeight,
- float scale, int padWidth, int padHeight, int originalWidth, int originalHeight)
+ int padWidth, int padHeight, int originalWidth, int originalHeight)
{
- json jsonResults = json::array();
- int validCount = 0;
+ vector finalResults;
for (size_t i = 0; i < results.size(); ++i) {
const auto& box = results[i];
@@ -293,15 +303,32 @@ namespace Yolo11sSegNS {
continue;
}
- json result;
- result["image_id"] = stoi(fileName);
- result["category_id"] = box.cocoClassId;
- result["bbox"] = { box.x1, box.y1, bboxWidth, bboxHeight };
- result["score"] = box.score;
- result["segmentation"] = segmentation;
- jsonResults.push_back(result);
+ SegmentationResult result;
+ result.imageId = stoi(fileName);
+ result.categoryId = box.cocoClassId;
+ result.x = box.x1;
+ result.y = box.y1;
+ result.width = bboxWidth;
+ result.height = bboxHeight;
+ result.score = box.score;
+ result.segmentation = segmentation;
+ finalResults.push_back(result);
+ }
+
+ return finalResults;
+ }
- validCount++;
+ void SaveJsonResults(const vector& results, const string& savePath, const string& fileName)
+ {
+ json jsonResults = json::array();
+ for (const auto& result : results) {
+ json jsonResult;
+ jsonResult["image_id"] = result.imageId;
+ jsonResult["category_id"] = result.categoryId;
+ jsonResult["bbox"] = { result.x, result.y, result.width, result.height };
+ jsonResult["score"] = result.score;
+ jsonResult["segmentation"] = result.segmentation;
+ jsonResults.push_back(jsonResult);
}
string jsonPath = savePath + "/" + fileName + ".json";
@@ -309,7 +336,7 @@ namespace Yolo11sSegNS {
if (jsonFile.is_open()) {
jsonFile << jsonResults.dump(2);
jsonFile.close();
- LOG(INFO) << "Saved " << validCount << " valid results to: " << jsonPath;
+ LOG(INFO) << "Saved " << results.size() << " valid results to: " << jsonPath;
} else {
LOG(WARNING) << "Failed to open json file: " << jsonPath;
}
@@ -474,8 +501,11 @@ namespace Yolo11sSegNS {
}
if (!saveJson.empty()) {
- SaveJsonResults(nmsResult, saveJson, fileName, protosData, targetWidth, targetHeight,
- scale, padWidth, padHeight, originalWidth, originalHeight);
+ // 结果计算与文件保存解耦。若后续需要内存串接,可直接使用 finalResults。
+ vector finalResults = BuildSegmentationResults(
+ nmsResult, fileName, protosData, targetWidth, targetHeight,
+ padWidth, padHeight, originalWidth, originalHeight);
+ SaveJsonResults(finalResults, saveJson, fileName);
}
LOG(INFO) << "====== Postprocessing Completed ======";
diff --git a/samples/samples_GPL/common/infer/post_process/yolov8s_seg_postprocess.cpp b/samples/samples_GPL/common/infer/post_process/yolov8s_seg_postprocess.cpp
index 3b5d5c76fca56799cd342a9ed2b7a4de0c3a826e..8c35bedd4d5b5d9dd5243c9e9033c4c21fa9fb45 100644
--- a/samples/samples_GPL/common/infer/post_process/yolov8s_seg_postprocess.cpp
+++ b/samples/samples_GPL/common/infer/post_process/yolov8s_seg_postprocess.cpp
@@ -57,6 +57,17 @@ namespace Yolov8sSegNS {
float input_x1 = 0.0f, input_y1 = 0.0f, input_x2 = 0.0f, input_y2 = 0.0f;
};
+ struct SegmentationResult {
+ int imageId = 0;
+ int categoryId = -1;
+ float x = 0.0f;
+ float y = 0.0f;
+ float width = 0.0f;
+ float height = 0.0f;
+ float score = 0.0f;
+ vector> segmentation;
+ };
+
const vector Yolo80ToCoco90 = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21,
22, 23, 24, 25, 27, 28, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
@@ -243,12 +254,11 @@ namespace Yolov8sSegNS {
return validSegments;
}
- void SaveJsonResults(const vector& results, const string& savePath, const string& fileName,
+ vector BuildSegmentationResults(const vector& results, const string& fileName,
const float* protos, int targetWidth, int targetHeight,
- float scale, int padWidth, int padHeight, int originalWidth, int originalHeight)
+ int padWidth, int padHeight, int originalWidth, int originalHeight)
{
- json jsonResults = json::array();
- int validCount = 0;
+ vector finalResults;
for (size_t i = 0; i < results.size(); ++i) {
const auto& box = results[i];
@@ -293,15 +303,32 @@ namespace Yolov8sSegNS {
continue;
}
- json result;
- result["image_id"] = stoi(fileName);
- result["category_id"] = box.cocoClassId;
- result["bbox"] = { box.x1, box.y1, bboxWidth, bboxHeight };
- result["score"] = box.score;
- result["segmentation"] = segmentation;
- jsonResults.push_back(result);
+ SegmentationResult result;
+ result.imageId = stoi(fileName);
+ result.categoryId = box.cocoClassId;
+ result.x = box.x1;
+ result.y = box.y1;
+ result.width = bboxWidth;
+ result.height = bboxHeight;
+ result.score = box.score;
+ result.segmentation = segmentation;
+ finalResults.push_back(result);
+ }
+
+ return finalResults;
+ }
- validCount++;
+ void SaveJsonResults(const vector& results, const string& savePath, const string& fileName)
+ {
+ json jsonResults = json::array();
+ for (const auto& result : results) {
+ json jsonResult;
+ jsonResult["image_id"] = result.imageId;
+ jsonResult["category_id"] = result.categoryId;
+ jsonResult["bbox"] = { result.x, result.y, result.width, result.height };
+ jsonResult["score"] = result.score;
+ jsonResult["segmentation"] = result.segmentation;
+ jsonResults.push_back(jsonResult);
}
string jsonPath = savePath + "/" + fileName + ".json";
@@ -309,7 +336,7 @@ namespace Yolov8sSegNS {
if (jsonFile.is_open()) {
jsonFile << jsonResults.dump(2);
jsonFile.close();
- LOG(INFO) << "Saved " << validCount << " valid results to: " << jsonPath;
+ LOG(INFO) << "Saved " << results.size() << " valid results to: " << jsonPath;
} else {
LOG(WARNING) << "Failed to open json file: " << jsonPath;
}
@@ -474,8 +501,11 @@ namespace Yolov8sSegNS {
}
if (!saveJson.empty()) {
- SaveJsonResults(nmsResult, saveJson, fileName, protosData, targetWidth, targetHeight,
- scale, padWidth, padHeight, originalWidth, originalHeight);
+ // 结果计算与文件保存解耦。若后续需要内存串接,可直接使用 finalResults。
+ vector finalResults = BuildSegmentationResults(
+ nmsResult, fileName, protosData, targetWidth, targetHeight,
+ padWidth, padHeight, originalWidth, originalHeight);
+ SaveJsonResults(finalResults, saveJson, fileName);
}
LOG(INFO) << "====== Postprocessing Completed ======";