利用pycocotools库计算MAP:生成coco格式 json文件数据集和计算map值

news2024/11/25 18:25:05

文章目录

    • 1.划分val数据集
    • 2. xml to json
    • 3. coco格式json文件
    • 4. 生成coco格式json文件
    • 5.使用pycocotools计算map
    • 6. 讨论

在目标检测任务中,需要通过Map指标判断模型的精度。为了测试engine文件推理结果的精度,本文介绍了如何使用pycocotools库计算Map,在此之前需要根据coco格式生成json文件。
必须按照coco格式生成json,顺序都要保持一致才行,否则报错不通过。

1.划分val数据集

在做验证时,需要提前划分好val数据集。本文采用labelimg工具画框,因此需要对xml和image文件进行划分。

# coding:utf-8

import os
import random
import argparse
from pathlib import Path
import shutil


def move_xml_img(save_path, xml_path, img_path, percent):
    total_img = os.listdir(img_path)
    num = len(total_img)
    percen_img = int(num * percent)
    print("Image Num: ", percen_img)
    train_lists = random.sample(total_img, percen_img)

    save_img_path = save_path + '/' + "images"
    save_xml_path = save_path + '/' + "xml"
    if not os.path.exists(save_img_path):
        os.makedirs(save_img_path)
    if not os.path.exists(save_xml_path):
        os.makedirs(save_xml_path)

    for list in train_lists:
        name = Path(list).stem
        save_img_path = save_path + '/' + "images" + '/'
        save_xml_path = save_path + '/' + "xml" + '/'
        img = img_path + name + ".bmp"
        xml = xml_path + name + ".xml"
        # print(img)
        shutil.copy(img, save_img_path)
        shutil.copy(xml, save_xml_path)


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--xml_path', default='E:/datasets/titanium/xml/', type=str, help='input xml label path')
    parser.add_argument('--img_path', default='E:/datasets/titanium/img/', type=str, help='input image label path')
    parser.add_argument('--save_path', default='E:/datasets/val_titanium/', type=str, help='output txt label path')
    opt = parser.parse_args()

    val_percent = 0.2  # 训练集所占比例,可自己进行调整

    xml_path = opt.xml_path
    img_path = opt.img_path
    save_path = opt.save_path

    val_path = save_path + '/' + "val"

    print("Val datasets start")
    move_xml_img(val_path, xml_path, img_path, val_percent)
    print("Val datasets end")
    print("End split train and val datasets")

  • xml_path:表示全部数据集xml的路径
  • img_path:表示全部数据集image的路径
  • save_path:表示划分之后val验证数据集存放的路径,会在save_path目录下生成image和xml路径
  • val_percent :调节验证集的数据,0.2表示比例

运行完上述代码会在save_path目录下生成val验证数据集。

2. xml to json

本文将val GT 数据集转cooc格式json文件都采用的txt转json的方式,因此需要将val数据集的xml文件转换为txt文件,提供代码如下:

# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
import os
from pathlib import Path

class_name = ['normal', 'iron', 'crystal', 'impurity']
get_name = {}


def convert(size, box):
    dw = 1. / (size[0])
    dh = 1. / (size[1])
    x = (box[0] + box[1]) / 2.0 - 1
    y = (box[2] + box[3]) / 2.0 - 1
    w = box[1] - box[0]
    h = box[3] - box[2]
    x = x * dw
    w = w * dw
    y = y * dh
    h = h * dh
    return x, y, w, h

def convert_annotation(xml_file, save_txt_file):
    in_file = open(xml_file, encoding='UTF-8')
    out_file = open(save_txt_file, 'w')
    tree = ET.parse(in_file)
    root = tree.getroot()
    if root:
        size = root.find('size')
        w = int(size.find('width').text)
        h = int(size.find('height').text)
        for obj in root.iter('object'):
            cls = obj.find('name').text
            if cls not in get_name:
                get_name[cls] = 1
            else:
                get_name[cls] += 1
            if cls not in class_name:
                continue
            cls_id = class_name.index(cls)
            xml_box = obj.find('bndbox')
            b = (float(xml_box.find('xmin').text), float(xml_box.find('xmax').text), float(xml_box.find('ymin').text),
                 float(xml_box.find('ymax').text))
            b1, b2, b3, b4 = b
            # 标注越界修正
            if b2 > w:
                b2 = w
            if b4 > h:
                b4 = h
            b = (b1, b2, b3, b4)
            bb = convert((w, h), b)
            out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')


if __name__ == '__main__':
    xml_path = "E:/datasets/val_titanium/xml/"
    save_txt_path = "E:/datasets/val_titanium/labels/"
    if not os.path.exists(save_txt_path):
        os.makedirs(save_txt_path)
    xml_lists = os.listdir(xml_path)
    for xml in xml_lists:
        # print("Do XML File:" + xml)
        name = Path(xml).stem
        xml_file = xml_path + xml
        txt_path = save_txt_path + name + ".txt"
        convert_annotation(xml_file, txt_path)
    print("XML File End:" + xml_path)
    print("Txt File Save On:" + save_txt_path)

    print("class name: ", class_name)
    print("get class name: ", get_name)

1. class_name:类别名称,需要将类别名称转换为int类型,根据自己的数据集进行排列
2. xml_path :xml文件路径
3. save_txt_path :保存txt文件路径

执行完上述代码之后,会在验证数据集目录下生成如下文件夹:
在这里插入图片描述
下面就可以将txt格式转化为cooc json格式了。

3. coco格式json文件

pycocotools对json文件必须严格要求coco格式,因此生成的json文件必须按照如下格式进行编写:

1. GT json(val验证数据集的参考数据)

{
	"info": ["none"],
	"license": ["none"],
	"images":
		 [{"file_name": , 
		 "width": , 
		 "height": , 
		 "id": 
		 }],
	"annotations": 
		[{"area": 2860,
		 "iscrowd": 0,
		 "image_id": ,
		 "bbox": [536, 29, 52, 55],
		 "category_id": 2,
		 "id": ,
		 "ignore": 1,
		 "segmentation": []},
	"categories":
	 	[{"id": ,
	 	 "name": ,
	 	  "supercategory":
	 	 }]
		 

其中:images内的id要和annotations中的id保持一致,categories内的id非0即可。area为目标面积

2.需要参考的数据格式(也就是待测试的json文件)

[{
"score": 0.0, 
"bbox": [536.0, 29.0, 52.0, 55.0],
 "image_id": , 
 "category_id": 2}, 

4. 生成coco格式json文件

在执行下述代码时,还需要在val验证数据集目录下建立classes.txt文件,里面存放目标类别名称。

类别A
类别B
类别C
类别D

import os
import cv2
import json
from tqdm import tqdm
import argparse


def val2coco(arg):
    root_path = arg.root_dir
    print("Loading data from ", root_path)

    assert os.path.exists(root_path)
    labels_path = os.path.join(root_path, 'labels')
    img_path = os.path.join(root_path, 'images')
    with open(os.path.join(root_path, 'classes.txt')) as f:
        classes = f.read().strip().split()
        print(classes)
    # images dir name
    indexes = os.listdir(img_path)

    # 用于保存所有数据的图片信息和标注信息
    val_dataset = {'info': ['none'], 'license': ['none'], 'images': [], 'annotations': [], 'categories': []}
    # 建立类别标签和数字id的对应关系, 类别id从0开始。
    for i, cls in enumerate(classes, 0):
        val_dataset['categories'].append({'id': i, 'name': cls, 'supercategory': 'cow'})

    # 标注的id
    for k, index in enumerate(tqdm(indexes)):
        # 支持 png jpg 格式的图片。
        txtFile = index.replace('images', 'labels').replace('.bmp', '.txt')

        im = cv2.imread(os.path.join(root_path, 'images/') + index)

        height, width, _ = im.shape

        # 添加图像的信息
        object_id = "".join(filter(str.isdigit, index))
        val_dataset['images'].append({'file_name': index,
                                      'width': width,
                                      'height': height,
                                      'id': int(object_id)})
        if not os.path.exists(os.path.join(labels_path, txtFile)):
            # 如没标签,跳过,只保留图片信息。
            continue
        with open(os.path.join(labels_path, txtFile), 'r') as fr:
            labelList = fr.readlines()
            for label in labelList:
                label = label.strip().split()
                # print(label)
                x = float(label[1])
                y = float(label[2])
                w = float(label[3])
                h = float(label[4])

                # convert x,y,w,h to x1,y1,x2,y2
                H, W, _ = im.shape
                x1 = int((x - w / 2) * W)
                y1 = int((y - h / 2) * H)
                x2 = int((x + w / 2) * W)
                y2 = int((y + h / 2) * H)
                x1 = max(0, x1)
                y1 = max(0, y1)
                x2 = min(W, x2)
                y2 = min(H, y2)

                cls_id = int(label[0])
                width = max(x1 - x2, x2 - x1)
                height = max(y1 - y2, y2 - y1)
                # print(width)
                val_dataset['annotations'].append({
                    'area': width * height,
                    'iscrowd': 0,
                    'image_id': int(object_id),
                    'bbox': [x1, y1, width, height],
                    'category_id': cls_id,
                    'id': int(object_id),
                    "ignore": 1,
                    'segmentation': []
                })
    # 保存结果
    with open(arg.save_val_path, 'w') as f:
        json.dump(val_dataset, f)
    print('Save annotation to {}'.format(arg.save_val_path))


def engine_result2coco(arg):
    txt_path = arg.result_txt_path
    txt_files = os.listdir(txt_path)
    results_list = []
    for txt_file in tqdm(txt_files):
        txt = os.path.join(txt_path, txt_file)
        res_dict = dict()
        with open(txt, 'r') as tf:
            labellists = tf.readlines()
            for labellist in labellists:
                label = labellist.strip().split()
                x = float(label[1])
                y = float(label[2])
                w = float(label[3])
                h = float(label[4])
                cls_id = int(label[0])
                image_id = "".join(filter(str.isdigit, txt_file))
                conf = label[5]
                res_dict['score'] = float(conf)
                res_dict['bbox'] = [float(x), float(y), float(round(w)), float(h)]
                res_dict['image_id'] = int(image_id)
                res_dict['category_id'] = int(cls_id)

            results_list.append(res_dict)

    with open(arg.save_result_path, 'w') as f:
        json.dump(results_list, f)
    print('Save annotation to {}'.format(arg.save_result_path))


def pt_result2coco(arg):
    pt_txt_path = arg.pt_txt_path
    img_path = str(pt_txt_path).replace('pt_labels', 'images')
    txt_files = os.listdir(pt_txt_path)
    results_list = []
    for txt_file in tqdm(txt_files):
        img_file = txt_file.replace('txt', 'bmp')
        img = cv2.imread(os.path.join(img_path, img_file))
        H, W, _ = img.shape
        txt = os.path.join(pt_txt_path, txt_file)
        res_dict = dict()
        with open(txt, 'r') as tf:
            labellists = tf.readlines()
            for labellist in labellists:
                label = labellist.strip().split()
                cls_id = int(label[0])
                cx, cy, w, h = float(label[1]) * W, float(label[2]) * H, float(label[3]) * W, float(label[4]) * H
                x1 = max(0, float(cx - w / 2.0))
                y1 = max(0, float(cy - h / 2.0))
                image_id = "".join(filter(str.isdigit, txt_file))
                res_dict['score'] = float(0.00)
                res_dict['bbox'] = [round(float(x1), 3), round(float(y1), 3), round(float(w), 3),
                                    round(float(h), 3)]
                res_dict['image_id'] = int(image_id)
                res_dict['category_id'] = int(cls_id)
            results_list.append(res_dict)

    with open(arg.save_pt_path, 'w') as f:
        json.dump(results_list, f)
    print('Save annotation to {}'.format(arg.save_pt_path))


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--root_dir', default='E:/datasets/val_titanium/', type=str,
                        help="root path of images and labels, include ./images and ./labels and classes.txt")
    parser.add_argument('--save_val_path', type=str, default='E:/datasets/val_titanium/annotations.json',
                        help="GT json path")
    parser.add_argument('--result_txt_path', type=str, default='E:/datasets/val_titanium/nvidia_1b_labels_1/',
                        help="engine txt path")
    parser.add_argument('--save_result_path', type=str, default='E:/datasets/val_titanium/nvidia_fp32_1b_2.json',
                        help="engine json path")
    parser.add_argument('--pt_txt_path', type=str, default='E:/datasets/val_titanium/pt_labels/',
                        help="pt txt path")
    parser.add_argument('--save_pt_path', type=str, default='E:/datasets/val_titanium/pt.json',
                        help="pt json path")

    arg = parser.parse_args()
    val2coco(arg)
    engine_result2coco(arg)
    pt_result2coco(arg)

1. val2coco函数生成GT val的json文件
2. engine_result2coco函数,是将yolov5 engine模型测试输出的txt文件转换为json格式
3. pt_result2coco函数将yolov5 pt文件通过detect.py生成的txt文件转换为json格式
4. image_id:代码中提取文件名中的int数字作为image_id,也可以将文件名转为str类型,但是需要所有的json都保持一致。

每个函数对应两个路径,修改好路径,根据自己的需求使用相应的函数。
engine_result2coco和pt_result2coco区别在与保存的txt文件是否归一化。

执行完上述代码之后,会在验证数据集目录下生成如下文件:
在这里插入图片描述

至此,满足pycocotools库的json文件已经生成,下一步介绍如何使用pycocotools工具测试json文件计算map。

5.使用pycocotools计算map

首先需要安装pycocotools库,推荐下列博主的方法:
https://blog.csdn.net/weixin_42715977/article/details/127727247

安装完毕即可使用pycocotools工具计算map值。

import os
from pycocotools.coco import COCO
from pycocotools.cocoeval import COCOeval
from collections import OrderedDict
import argparse


class COCOResults(object):
    METRICS = {
        "bbox": ["AP", "AP50", "AP75", "APs", "APm", "APl"],
        "segm": ["AP", "AP50", "AP75", "APs", "APm", "APl"],
        "box_proposal": [
            "AR@100",
            "ARs@100",
            "ARm@100",
            "ARl@100",
            "AR@1000",
            "ARs@1000",
            "ARm@1000",
            "ARl@1000",
        ],
        "keypoints": ["AP", "AP50", "AP75", "APm", "APl"],
    }

    def __init__(self, *iou_types):
        allowed_types = ("box_proposal", "bbox", "segm", "keypoints")
        assert all(iou_type in allowed_types for iou_type in iou_types)
        results = OrderedDict()
        for iou_type in iou_types:
            results[iou_type] = OrderedDict(
                [(metric, -1) for metric in COCOResults.METRICS[iou_type]]
            )
        self.results = results

    def update(self, coco_eval):
        if coco_eval is None:
            return
        from pycocotools.cocoeval import COCOeval

        assert isinstance(coco_eval, COCOeval)
        s = coco_eval.stats
        iou_type = coco_eval.params.iouType
        res = self.results[iou_type]
        metrics = COCOResults.METRICS[iou_type]
        for idx, metric in enumerate(metrics):
            res[metric] = s[idx]

    def __repr__(self):
        results = '\n'
        for task, metrics in self.results.items():
            results += 'Task: {}\n'.format(task)
            metric_names = metrics.keys()
            metric_vals = ['{:.4f}'.format(v) for v in metrics.values()]
            results += (', '.join(metric_names) + '\n')
            results += (', '.join(metric_vals) + '\n')
        return results


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--log', default='E:/datasets/val_titanium/annotations.json',
                        help='GT path(s)')
    parser.add_argument('--anno', default='E:/datasets/val_titanium/pt.json',
                        help='anno path(s)')
    parser.add_argument('--image-dir', default='E:/datasets/val_titanium/images/',
                        help='image path(s)')
    args = parser.parse_args()
    coco = COCO(args.anno)
    results = COCOResults('bbox')
    coco_dt = coco.loadRes(args.log)
    coco_eval = COCOeval(coco, coco_dt, 'bbox')
    imagelist = os.listdir(args.image_dir)
    # coco_eval.params.imgIds = [int(Path(x).stem) for x in imagelist if x.endswith('jpg')]
    coco_eval.evaluate()
    coco_eval.accumulate()
    coco_eval.summarize()
    results.update(coco_eval)
    print(results)

1. log路径为GT 目标的json文件
2. anno路径为待测试结果的json文件
3. image-dir为测试图片的路径

运行完上述代码会打印计算的map值:
在这里插入图片描述

6. 讨论

在测试中发现pycocotools库计算出来的map值比yolov5 val.py计算出来的map值低,具体原因不清楚。
下列博客提供了一种讨论。
https://blog.csdn.net/qq_34062683/article/details/128907714

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/584698.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

4年测试,裸辞后已失业3个月.....

我做测试4年,一线城市薪水拿到15K,中间还修了一个专升本,这个年限不说资深肯定也是配得上经验丰富的。今年行情不好人尽皆知,但我还是对我的薪水不是很满意,于是打算出去面试,希望可以搏一个高薪。 但真到面…

vscode使用git

文章目录 前言一、配置ssh-key二、GitHub上创建一个空的仓库三、链接GitHub,并提交本地文件 前言 从今天开始学习前端知识,学会先使用工具很重要,今天尝试了下用vscode链接GitHub,实现代码管理。 前提: 1、需要先下载…

科技云报道:2023年安全运营之风将吹向何方?

科技云报道原创。 在实战演练成为常态化的背景下,建立实战化安全运营能力是一个绕不开的话题。作为网络安全发展的时代产物,安全运营被认为是解决现有挑战的有利方法。 但随着有安全形势、政策导向、发展需求的变化,安全运营的理念也在不断演…

深度解析如何通过财务共享建设助推企业数智化转型

国务院国资委印发了《关于中央企业加快建设世界一流财务管理体系的指导意见》(以下简称《意见》),文中明确指出了数智化转型的路径,即“积极探索依托财务共享实现财务数智化转型的有效路径,推进共享模式、流程和技术创…

Arnold图像置乱的MATLAB实现

这件事情的起因是这样的,我需要研究一下各种图像置乱的算法。然后在知乎上找到了一篇关于Arnold变化的文章,但是呢,这个人实际上是卖资料,代做大作业的。详细的代码根部不给你,则给我气坏了,必须要手动实现…

Java泛型 <T> T、 T、<T>的用法

我们聊聊Java泛型中的 T 是什么? T 在Java泛型中,被称作类型变量。那么什么又是类型变量? 类型变量在整个类的定义中用于指定方法的返回类型,同时也可以指定字段和局部变量的类型,我们可以用具体的类型来替换类型变量…

数据库【数据操作】

这篇文章呢是小编对正在学习的数据库的实验内容进行一个简单的记录,以便后期复习,希望小编的这些例子也可以帮助到正在和我一样学习数据库的友友们哦~ 实验目的: (1)掌握使用T-SQL插入数据,修…

Arthas

Arthas 概述 Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。 当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决: 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?我改的代码为什么没有…

无纸化、自动化、智能化|WMS系统升级你的仓储管理模式

随着物流行业的不断发展,现代仓储管理已经从传统的手工操作逐渐转向无纸化、自动化、智能化管理。WMS系统作为一种全新的仓储管理模式,正在逐步被企业所接受和运用。 什么是WMS系统? WMS系统全称为Warehouse Management System,即…

汇编寄存器之内存访问

1.内存中字的存储: 在CPU中用一个16位寄存器来存储一个字, 高8位存高字节,低8位存低字节 如AX寄存器存在一个字,那么AH存高字节,AL存低字节 在内存中存储字时是用两个连续的字节来存储字的, 这个字的低字节存在低单元,高字节存在高单元. 如下表示: 内存单元编号 单元中…

开发工程师-常用算法基本思想 -分类-时间复杂度与空间复杂度概述

文章目录 插入排序选择排序交换排序归并排序各种排序算法时间复杂度、空间复杂度以及稳定性分类 插入排序 1.基本思想:将一个数据插入到一个有序的数据列表,得到一个新的有序列表 2.分类:直接插入排序、希尔排序 选择排序 1.工作原理&…

机器学习基础知识之多模型性能对比评价方法

文章目录 1、交叉验证t检验2、Friedman检验与Nemenyi后续检验 在进行预测或分类对比实验时,通常需要比较两个或两个以上的模型性能,因此,下面将介绍两个常用的多模型性能对比评价方法,一种是交叉验证t检验,该方法主要用…

英语单词365-9

英语单词365-9 9.1 manipulate_哔哩哔哩_bilibili

网络安全就业会不会容易被淘汰?

先说结论:不会 作为一名资深的网络安全工程师,我的观点是网络安全是一个长期持续的领域,而不是一个很容易被淘汰的职业。可能会随着技术的进步和新的威胁的出现而有所改变,但作为一个专门从事网络安全领域的人员,我们…

SQL面试必备:100道高频考题解析

前言 在众多IT职场中,SQL技术一直是一个非常重要的技能点。如果你正在准备SQL相关的面试,那么这份“SQL面试 100 问”绝对是你不能错过的宝藏! 这份清单涵盖了100道高频考题,从基础知识到复杂应用都有所涉及,帮助你全…

数据结构·第3章【栈和队列】

栈 顺序栈 栈(Stack)是限定仅在表的一端进行插入或删除操作的线性表。通常称插入删除的一端为栈顶(top),另一端称为栈底(bottom)。 typedef struct{DataType data[StackSize];int top; }Se…

缩减虚拟机堆空间的方式,缓解32位cpu上虚拟内存地址空间限制导致的内存分配失败崩溃

缩减虚拟机堆空间的方式,缓解32位cpu上虚拟内存地址空间限制导致的内存分配失败崩溃 前言Matrix使用说明效果验证 前言 瑞芯微平台应用开发,目前RK3288芯片应用还是比较广泛(成本低),它是一个32位cpu,并且…

Halcon中的一些3D算子

一、记录一些Halcon里的关于3D的算子 1.read_object_model_3d 从文件读取一个3d模型 如下图,读的一个ply文件出来是个3d点云模型 2.visualize_object_model_3d 交互式展示3d模型 即上个算子读出来后,通过这个算子可以把3d模型显示出来旋转、平移&am…

SpringFramework 中CollectionUtils 工具类的使用

CollectionUtils是Spring框架中的一个工具类,提供了一系列对集合的操作方法。 import org.springframework.util.CollectionUtils;import java.util.*;public class CollectionUtilDemo {public static void main(String[] args) {//判断一个集合或Map是否为空&…

OWASP ZAP alerts

前提 使用OWASP ZAP对网站进行安全扫描,扫描后发现一些警告。 使用警告名称在百度进行搜索就能看到在OWASP ZAP网站上对应警告的解释。 可以在如下地址输入alert查询 https://www.zaproxy.org/docs/alerts/ Missing Anti-clickjacking Header 见https://www.zap…