您现在的位置是:首页 > 正文

【深度学习标注数据处理】 BoundingBox标注数据中的xml、txt存储文件的处理汇总

2024-04-01 01:24:45阅读 4

目录

一、BoundingBox 的 Label Img 形式--- xml 文件

1.1、labelImg的安装

1.2、xml文件内容注释

1.3、对xml文件操作

二、BoundingBox 的 VOC 形式--- txt 文件

三、两BoundingBox的IOU的计算

3.1、方法1

3.2、方法2

一、BoundingBox 的 Label Img 形式--- xml 文件

xml文件中矩形框坐标的获取比较简单。

  1. xml文件可采用标注软件labelImg进行生成
  2. xml中记录了被标注图像信息和标注的信息

1.1、labelImg的安装

(着重说Ubuntu下的一个安装,别的版本可参照上面说到的labelImg博客)

现在就可以Terminal下打开看看了,点击Open打开一张带标记图片,如图:

根据上面的英文,也都该知道怎么用,其中有些省事省力的工作,就是:

  1. 先给待label图片做好命名,放在同一文件夹;
  2. 然后设定OpenDir和待保存.xml文件夹下ChangeSaveDir;

  3. 如果是一个类别,可使用Use Default label,这样提高标注效率。

1.2、xml文件内容注释

下面对保存的xml文件内容进行简单的注视解析

从图片中来,再到图片中去,我们来找一下对应关系

1.3、对xml文件读取

# -*- coding:utf8 -*-
import xml.etree.ElementTree as ET


def readXML(xml_file_path):
    tree = ET.parse(xml_file_path)
    root = tree.getroot()

    for size in root.iter('size'):
        width = int(size.find('width').text)
        height = int(size.find('height').text)
    for box in root.iter('bndbox'):
        xmin = int(box.find('xmin').text)
        ymin = int(box.find('ymin').text)
        xmax = int(box.find('xmax').text)
        ymax = int(box.find('ymax').text)

二、BoundingBox 的 VOC 形式--- txt 文件

内容按行存储,依次是label,x_center,y_center,x_relative,y_relative

直观换算后是这样的矩形框

三、两BoundingBox的IOU的计算

3.1、方法1

得到一个框与另一个框的iou结果


import numpy as np
# ############################################################
# # IOU
# ############################################################
def two_Box_iou(list_a, list_b):
    """Compute the iou of two boxes.
    """
    # 获取矩形框交集对应的顶点坐标(intersection)
    xmin1, ymin1, xmax1, ymax1 = int(list_a[0]),int(list_a[1]), int(list_a[2]), int(list_a[3])
    xmin2, ymin2, xmax2, ymax2 = int(list_b[0]),int(list_b[1]), int(list_b[2]), int(list_b[3])

    xx1 = np.max([xmin1, xmin2])
    yy1 = np.max([ymin1, ymin2])
    xx2 = np.min([xmax1, xmax2])
    yy2 = np.min([ymax1, ymax2])

    # 计算两个矩形框面积
    area1 = (xmax1 - xmin1 + 1) * (ymax1 - ymin1 + 1)
    area2 = (xmax2 - xmin2 + 1) * (ymax2 - ymin2 + 1)

    # 计算交集面积
    inter_area = (np.max([0, xx2 - xx1])) * (np.max([0, yy2 - yy1]))
    # 计算交并比
    iou = inter_area / (area1 + area2 - inter_area + 1e-6)
    return iou
#
list_a = [321,296,387,342]
list_b = [328,313,359,332]
rst_IOU = two_Box_iou(list_a, list_b)
print(rst_IOU)

3.2、方法2

与法1不同是:得到一系列数组框与另一系列框的iou结果,计算任意两两之间的iou值使用场景更丰富


import numpy as np
import cv2

def get_iou_arr(arr_box_a, arr_box_b):
    """
    :param arr_box_a: (n,4)
    :param arr_box_b: (n,4)
    :return: 顺序性iou
    """
    assert arr_box_a.shape[-1] == 4 and arr_box_b.shape[-1] == 4, "a box should be described by 4 nums"
    if arr_box_a.ndim < 2:
        arr_box_a = arr_box_a.reshape([1, -1])
    if arr_box_b.ndim < 2:
        arr_box_b = arr_box_b.reshape([1, -1])

    max_v = np.max((np.max(arr_box_a), np.max(arr_box_b.max())))
    arr_box_a = np.expand_dims(arr_box_a, 1) / max_v
    arr_box_b = np.expand_dims(arr_box_b, 0) / max_v
    # 交集矩形框
    left_top = np.maximum(arr_box_a[..., :2], arr_box_b[..., :2])
    right_down = np.minimum(arr_box_a[..., 2:], arr_box_b[..., 2:])
    cover_wh = np.clip(right_down - left_top, 0, 1)
    s_cover = cover_wh[..., 0] * cover_wh[..., 1]

    # a、b面积
    wh_a = arr_box_a[..., 2:] - arr_box_a[..., :2]
    s_a = wh_a[..., 0] * wh_a[..., 1]
    wh_b = arr_box_b[..., 2:] - arr_box_b[..., :2]
    s_b = wh_b[..., 0] * wh_b[..., 1]
    iou = s_cover / (s_a + s_b - s_cover)
    return iou

def aibox_match_gt(ai_box, gt_box, gt_class, iou_threshold):
    if gt_box.size == 0:
        gt_class = np.array(['-1'])
        gt_box = np.array([[0, 0, 0, 0]])

    print(ai_box)
    print(gt_box)
    iou_arr = get_iou_arr(ai_box, gt_box)
    print('iou_arr:', iou_arr)
    iou_max = iou_arr.max(axis=1)   # axis=1 按行  axis=0 按列   返回一维数组中最大元素

    ai_loc_res = iou_max > iou_threshold
    select_idx = iou_arr.argmax(axis=1)     # .argmax 返回一维数组中最大元素的索引位置
    gt_class_tiled = gt_class[select_idx]

    return gt_class_tiled, ai_loc_res

if __name__=='__main__':
    ai_box = np.array([[210, 67, 682, 305], [318, 307, 627, 540]])
    gt_box = np.array([[229, 77, 662, 275], [318, 297, 617, 560]])
    gt_class = np.array(['glass', 'face'])
    gt_class_matched, loc_res = aibox_match_gt(ai_box, gt_box, gt_class, iou_threshold=0.3)
    print(gt_class_matched, loc_res)

打印内容:

ai_box: [[210  67 682 305]
 [318 307 627 540]]
gt_box: [[229  77 662 275]
 [318 297 617 560]]
iou_arr: [[0.76319257 0.0126842 ]
 [0.         0.86043697]]
['glass' 'face'] [ True  True]

对预测边框与标注边框,打印到原图上,进行查看,如下代码:

def draw():
    ai_box = np.array([[210, 67, 682, 305], [318, 307, 627, 540]])
    gt_box = np.array([[229, 77, 662, 275], [318, 297, 617, 560]])

    image = cv2.imread(r'E:\temp\jpeg\girl.jpg')
    for e in ai_box.tolist():
        x, y, x2, y2 = e
        cv2.rectangle(image, (int(x), int(y)), (int(x2), int(y2)), (0, 0, 255), thickness=2)

    for e in gt_box.tolist():
        x, y, x2, y2 = e
        cv2.rectangle(image, (int(x), int(y)), (int(x2), int(y2)), (255, 0, 0), thickness=2)
    cv2.imwrite(r'E:\temp\jpeg\girl_ai_gt.jpg', image)

展示结果如下:红色框是ai结果,蓝色框是gt标注内容

网站文章

  • 线程和进程有什么区别(简单介绍)

    线程和进程有什么区别(简单介绍)

    线程和进程有什么区别(简单介绍) 简单介绍 一、线程的基本概念   线程是进程中执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。 ...

    2024-04-01 01:24:34
  • 五类Kafka客户端API作用和区别(简介)

    五类Kafka客户端API作用和区别(简介)

    2024-04-01 01:24:29
  • Caused by: org.xml.sax.SAXParseException; lineNumber: 2; columnNumber: 6; 不允许有匹配 [xX][mM][lL] 的处理指令目

    Caused by: org.xml.sax.SAXParseException; lineNumber: 2; columnNumber: 6; 不允许有匹配 [xX][mM][lL] 的处理指令目

    Caused by: org.xml.sax.SAXParseException; lineNumber: 2; columnNumber: 6; 不允许有匹配 [xX][mM][lL] 的处理指令目错误原因:,解决办法:删除第一行的空格。

    2024-04-01 01:24:03
  • 自学Java到成功就业,第一年的路你应该这么走!

    自学Java到成功就业,第一年的路你应该这么走!

    首先初识语法的阶段,必须要学会怎么操作对象,操作if和for,操作list set map,然后是线程、IO和jdbc什么的,其余的,若是一时不理解,可以后边需要时再学。这阶段完了,你可以写些能在控制...

    2024-04-01 01:23:55
  • 判断一个树是完全二叉树

    判断一个树是完全二叉树

    1064 甲级Complete Binary Search Tree(30分) 题目链接:https://pintia.cn/problem-sets/994805342720868352/probl...

    2024-04-01 01:23:48
  • idea中如何进行修改git的密码以及怎么删除idea中的git

    idea中如何进行修改git的密码以及怎么删除idea中的git

    idea中如何进行修改git的密码

    2024-04-01 01:23:39
  • vue 实例之路由,动态路由,嵌套路由,命名路由,命名视图

    vue 实例之路由,动态路由,嵌套路由,命名路由,命名视图

    认识路由路由的本质是一种 对应关系 ,程序开发分为前端路由和后端路由。url 地址和真实的资源之间就有一种对应的关系,就是路由 。什么是前端路由前端路由就是把不同路由对应不同的内容或页面的任务交给前端...

    2024-04-01 01:23:17
  • 插件化皮肤加载框架Skin-Loader

    插件化皮肤加载框架Skin-Loader

    一个动态加载皮肤资源的换肤框架。 支持 Material Design 风格控件一键换肤示例图(后续补上) 用法// 项目引用dependencies { compile 'com.github.LidongWen:Skin-Loader:0.0.1'}// 根目录下引用allprojects { repositories { jcenter()

    2024-04-01 01:23:09
  • mock进行单元测试经验之谈

    mock进行单元测试经验之谈

    单元测试是指对软件中的最小可测试单元在与程序其他部分相隔离的情况下进行检查和验证的工作,这里的最小可测试单元通常是指函数或者类。驱动代码是用来调用被测函数的,而桩代码和 Mock 代码是用来代替被测函...

    2024-04-01 01:23:00
  • MySQL事物本质_【MySQL—原理】事务

    事务可由一条非常简单的SQL语句组成,也可以由一组复杂的SQL语句组成。事务是访问并更新数据库中各种数据项的一个程序执行单元。在事务中的操作,要么都做修改,要么都不做,这就是事务的目的,也是事务模型区...

    2024-04-01 01:22:36