表格关键点检测之COCO数据集转换

一、项目简介

比赛地址:https://aistudio.baidu.com/aistudio/competition/detail/702/0/introduction

生活中,扫描技术越来越常见,通过手机就能将图片转化为可编辑的文档等;但是现在的技术在处理带有表格类型的文字的时候往往没有那么灵敏,把完整表格拆分成难以使用的零散个体似乎很常见又令人苦恼。本次比赛旨在解决这个问题,通过万能的算法,准确地识别表格在图片中的位置并标注。

二、数据集简介

在本次比赛最新发布的数据集中,所有的图像数据由真实场景采集及开源程序自动生成,本次比赛不限制大家使用额外的训练数据来优化模型。
备注: 百度网盘坚持隐私红线,不会收集或者提供任何用户存储在百度网盘中的文件数据。

1.数据描述

train_data
├── annos.txt
├── imgs

本次比赛最新发布的数据集共包含训练集、A榜测试集、B榜测试集三个部分,其中训练集共10000张图片,A榜测试集共500张图片,B榜测试集500张图片;
imgs目录下为所有训练图片;
annos.txt 为标注文件,json格式,格式示例如下:

{
    "a.jpg": [  # 图片文件名称,不含路径
        {
            'box': [xmin, ymin, xmax,ymax],  # 表格box位置,(xmin, ymin)为box左上点,(xmax, ymax)为box右下点
            'lb': [x, y],  # 表格left bottom点,即左下顶点
            'lt': [x, y],  # 表格left top点,即左上顶点
            'rt': [x, y],  # 表格right top点,即右上顶点
            'rb': [x, y]  # 表格right bottom点,即右下顶点
        },
        {
            'box': [xmin, ymin, xmax, ymax],
            'lb': [x, y],
            'lt': [x, y],
            'rt': [x, y],
            'rb': [x, y]
        }
    ],
    
    "b.jpg": [{
        'box': [xmin, ymin, xmax, ymax],
        'lb': [x, y],
        'lt': [x, y],
        'rt': [x, y],
        'rb': [x, y]
    }, {
        'box': [xmin, ymin, xmax, ymax],
        'lb': [x, y],
        'lt': [x, y],
        'rt': [x, y],
        'rb': [x, y]
    }]
}

2.数据下载

数据集类型开放时间下载地址
训练集2022/11/29 12:00点击下载
A榜测试集2022/11/29 12:00点击下载
B榜测试集2023/2/28 12:00点击下载

三、数据查看

1.解压缩数据

!unzip -qoa data/data182865/train.zip -d data/

2.查看图片

通过观察图片可知:

  • 每张图仅有一个表格
    import os
    import cv2
    import json
    import matplotlib.pyplot as plt
    %matplotlib inline


    img = cv2.imread('data/train/imgs/border_0_01KINTN2ZW6H9WA6LK7Z.jpg')
    plt.imshow(img[:,:,::-1])
    plt.show()
    # height width
    print(img.shape)

在这里插入图片描述

(1039, 1024, 3)

3.查看标注文件

  • 训练集共有10000张图片
# 查看训练集数量
train_file = 'data/train/annos.txt'
with open(train_file, 'r', encoding='utf-8') as f:
    json_data = json.load(f)
print(len(json_data))   
10000
# 打印bbox和关键点
print(json_data["border_0_01KINTN2ZW6H9WA6LK7Z.jpg"])
[{'box': [357, 8, 665, 1027], 'lb': [525, 1027], 'lt': [357, 1004], 'rt': [496, 8], 'rb': [665, 32]}]

四、转COCO数据集

COCO的 全称是Common Objects in COntext,是微软团队提供的一个可以用来进行图像识别的数据集。MS COCO数据集中的图像分为训练、验证和测试集。COCO通过在Flickr上搜索80个对象类别和各种场景类型来收集图像,其使用了亚马逊的Mechanical Turk(AMT)。

COCO通过大量使用Amazon Mechanical Turk来收集数据。COCO数据集现在有3种标注类型:object instances(目标实例), object keypoints(目标上的关键点), 和image captions(看图说话),使用JSON文件存储。

1.基本的JSON结构体类型 (共享部分)

object instances(目标实例)、object keypoints(目标上的关键点)、image captions(看图说话)这3种类型共享这些基本类型:info、image、license。而annotation类型则呈现出了多态:

{
    "info": info, # dict
    "licenses": [license], # list ,内部是dict
    "images": [image], # list ,内部是dict
    "annotations": [annotation], # list ,内部是dict
    "categories": # list ,内部是dict
}

    
info{ # 数据集信息描述
        "description": str, # 数据集描述
        "url": str, # 下载地址
        "version": str, # 版本
        "year": int, # 年份
        "contributor": str, # 提供者
        "date_created": str # 数据创建日期
    },

license{
    "id": int,
    "name": str,
    "url": str,
} 
image{
    "id": int,# 图片的ID编号(每张图片ID是唯一的)
    "width": int,# 宽
    "height": int,# 高
    "file_name": str,# 图片名
    "license": int,
    "flickr_url": str,# flickr网路地址
    "coco_url": str,
    "date_captured": datetime,# 数据获取日期
}
# 定义coco集合
coco=dict()

1.1 info

info类型,比如一个info类型的实例

{
    "info": info,
    "licenses": [license],
    "images": [image],#数组元素的数量等同于划入训练集(或者测试集)的图片的数量;
    "annotations": [annotation],#数组元素的数量等同于训练集(或者测试集)中bounding box的数量;
    "categories": [category]
}
info={
	"description":"This is stable 1.0 version of the 2014 MS COCO dataset.",
	"url":"http:\/\/mscoco.org",
	"version":"1.0","year":2014,
	"contributor":"Microsoft COCO group",
	"date_created":"2022-12-26"
}
# 定义coco的info部分,并加入coco集合

info={
    "description":"This is stable 1.0 version of the 2014 MS COCO dataset.",
    "url":"http:\/\/mscoco.org",
    "version":"1.0","year":2014,
    "contributor":"Microsoft COCO group",
    "date_created":"2022-12-26"
}
coco['info']=info

1.2 licenses

licenses是包含多个license实例的数组,对于一个license类型的实例

license{
    "id": int,
    "name": str,
    "url": str,
} 

例如

licenses={
	"url":"http:\/\/creativecommons.org\/licenses\/by-nc-sa\/2.0\/",
	"id":1,
	"name":"Attribution-NonCommercial-ShareAlike License"
}
  • license跟图像 id 有关,定义函数如下:
def generate_license(image_id):
    license = {
        "url": "http:\/\/goingtodo.cn\/licenses\/",
        "id": image_id,
        "name": "nothing just awsome License"
    }
    return license

1.3 Image

Images是包含多个image实例的数组,对于一个image类型的实例:

image{
    "id": int,# 图片的ID编号(每张图片ID是唯一的)
    "width": int,# 宽
    "height": int,# 高
    "file_name": str,# 图片名
    "license": int,
    "flickr_url": str,# flickr网路地址
    "coco_url": str,
    "date_captured": datetime,# 数据获取日期
}
  • Image跟图像 id 以及图片尺寸有关,定义函数如下:
import cv2


def image_info(image_name, image_id):
    # img = cv2.imread(image_name)
    # 加入相对路径
    img = cv2.imread(os.path.join('data/train/imgs', image_name))
    image_info = {"id": image_id,  # 图片的ID编号(每张图片ID是唯一的)
        "width": img.shape[1],  # 宽
        "height": img.shape[0],  # 高
        "file_name": image_name,  # 图片名
        "license": "awsome license",
        "flickr_url": "no url",  # flickr网路地址
        "coco_url": "no url",
        "date_captured": "2022-12-26"  # 数据获取日期
        }
    return image_info

1.4 categories

categories是一个包含多个category实例的数组,而category结构体描述如下:

"categories":{ # 类别描述
    "id": int,# 类对应的id (0 默认为背景)
    "name": str, # 子类别
    "supercategory": str,# 主类别
}

从instances_val2017.json文件中摘出的2个category实例如下所示:

{
	"supercategory": "person",
	"id": 1,
	"name": "person"
},
{
	"supercategory": "vehicle",
	"id": 2,
	"name": "bicycle"
},
  • categories跟图像id有关
def generate_categories(image_id):
    cat = {
        "supercategory": "table",
        "id": image_id,
        "name": "table",
        "keypoints": ['lb', 'lt' , 'rt' , 'rb'],
	    "skeleton":  [[1,2],[2,3],[3,4],[4,1]]
    }
    return cat

1.5 annotations

annotation{
    "id": int,     # int 图片中每个被标记物体的id编号
    "image_id": int, # int 该物体所在图片的编号
    "category_id": int,# int 被标记物体的类别id编号
    "segmentation": RLE or [polygon],#分割区域的坐标,对象的边界点(边界多边形)
    "area": float,# float 被检测物体的面积
    "bbox": [x,y,width,height],# 目标检测框的坐标信息
    "iscrowd": 0 or 1,# 0 or 1 目标是否被遮盖,默认为0
}

这个类型中的annotation结构体包含了Object Instance中annotation结构体的所有字段,再加上2个额外的字段。

新增的keypoints是一个长度为3*k的数组,其中k是category中keypoints的总数量。每一个keypoint是一个长度为3的数组,第一和第二个元素分别是x和y坐标值,第三个元素是个标志位v,v为0时表示这个关键点没有标注(这种情况下x=y=v=0),v为1时表示这个关键点标注了但是不可见(被遮挡了),v为2时表示这个关键点标注了同时也可见。

num_keypoints表示这个目标上被标注的关键点的数量(v>0),比较小的目标上可能就无法标注关键点。

annotation{
    "keypoints": [x1,y1,v1,...],
    "num_keypoints": int,
    "id": int,
    "image_id": int,
    "category_id": int,
    "segmentation": RLE or [polygon],
    "area": float,
    "bbox": [x,y,width,height],
    "iscrowd": 0 or 1,
}

此次需要的数据如下:

  • bbox
  • keypoints

2.组装coco

前面定义了coco集合,加入了info,现在组装其他数据,主要有:

  • license
  • image
  • annotation

2.1定义大项集合

coco['images'] = []
coco['annotations'] = []
coco['categories'] = []
coco['licenses'] = []

2.2 遍历组装

info前面已经组装了,现在组装其他部分:

  • images
  • categories
  • licenses
  • annotations
    • 目标检测
    • 关键点
# 遍历组装
ii=0

for i in json_data.keys():
    image_temp=dict()

    # 1.coco['images']信息组装
    # 获取图片信息
    # print(i, ii)
    image_info_some = image_info(image_name=i, image_id=ii)
    # 添加到 coco['images'] 数组
    coco['images'].append(image_info_some)
    
    # 2.coco['images']信息组装
    # 类别信息获取
    cat_info=generate_categories(image_id=ii)
    # 添加到 coco['categories'] 数组
    coco['categories'].append(cat_info)

    # 3.coco['licenses'] 信息组装
    # 版权信息获取
    lic_info=generate_license(image_id=ii)
    # 添加到 coco['licenses'] 数组
    coco['licenses'].append(lic_info)

    # 4.coco['annotations'] 信息组装
    # 标注信息集合定义    
    annotation_temp=dict()

    # 4.1 annotations公共部分信息
    # 标注id信息
    annotation_temp['id']=ii
    # 图像id
    annotation_temp['image_id']=ii
    # 关键点个数 4个,上下左右 
    annotation_temp['num_keypoints']=4
    # 0好像是背景
    annotation_temp['category_id']=1

    image_anno= json_data[i][0]

    # 4.2 annotations目标检测部分
    # bbox,目标检测框的坐标信息,一张图只有一个表格,所以就这样了。
    # print(image_anno)
    # [{'box': [987, 2135, 2343, 2550], 'lb': [987, 2542], 'lt': [1029, 2135], 'rt': [2264, 2139], 'rb': [2343, 2550]}]

    annotation_temp['bbox']=image_anno['box']
    # 0 or 1 目标是否被遮盖,默认为0
    annotation_temp['iscrowd']=0
    # 被标记物体的类别id编号

    # 4.3 annotations关键点部分部分
    # 新增的keypoints是一个长度为3*k的数组,其中k是category中keypoints的总数量。
    # 每一个keypoint是一个长度为3的数组,第一和第二个元素分别是x和y坐标值,第三个元素是个标志位v,v为0时表示这个关键点没有标注(这种情况下x=y=v=0),v为1时表示这个关键点标注了但是不可见(被遮挡了),v为2时表示这个关键点标注了同时也可见。
    keypoints=[image_anno['lb'][0],image_anno['lb'][1],2, image_anno['lt'][0],image_anno['lt'][1],2,image_anno['rt'][0],image_anno['rt'][1],2,image_anno['rb'][0],image_anno['rb'][1],2]
    # 加入annotation标注集合
    annotation_temp['keypoints']=keypoints
    coco['annotations'].append(annotation_temp)
    ii=ii+1

3.保存标注文件

import json

ff = open('table_keypoints_val2017.json', 'w')
ff.write(json.dumps(coco, ensure_ascii=False ) + '\n')
ff.close()

4.目录重构

%cd ~
!mkdir table_coco
!mv data/train table_coco
/home/aistudio
!mv table_coco/train table_coco/train2017
!mkdir table_coco/annotations
!mv table_keypoints_val2017.json table_coco/annotations/table_keypoints_val2017.json
!mv table_coco/train2017/imgs/* table_coco/train2017/
!rm table_coco/train2017/annos.txt
! ls table_coco
annotations  train2017

五、数据分析

1.cocoapi安装

!git clone https://gitee.com/yu_duo/cocoapi.git
%cd cocoapi/PythonAPI
!pip install -e ./
/home/aistudio/cocoapi/PythonAPI
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Obtaining file:///home/aistudio/cocoapi/PythonAPI
  Preparing metadata (setup.py) ... [?25ldone
[?25hInstalling collected packages: pycocotools
  Attempting uninstall: pycocotools
    Found existing installation: pycocotools 2.0
    Uninstalling pycocotools-2.0:
      Successfully uninstalled pycocotools-2.0
  Running setup.py develop for pycocotools
Successfully installed pycocotools-2.0

[1m[[0m[34;49mnotice[0m[1;39;49m][0m[39;49m A new release of pip available: [0m[31;49m22.1.2[0m[39;49m -> [0m[32;49m22.3.1[0m
[1m[[0m[34;49mnotice[0m[1;39;49m][0m[39;49m To update, run: [0m[32;49mpip install --upgrade pip[0m
!pip list|grep coco
pycocotools                    2.0             /home/aistudio/cocoapi/PythonAPI

2.校验

%matplotlib inline
from pycocotools.coco import COCO
import numpy as np
import matplotlib.pyplot as plt
dataDir='table_coco'
dataType='table_keypoints_val2017'
annFile='{}/annotations/{}.json'.format(dataDir,dataType)
# initialize COCO api for instance annotations
%cd ~
coco=COCO(annFile)
/home/aistudio
loading annotations into memory...
Done (t=0.33s)
creating index...
index created!

x…
index created!

# get all images containing given categories, select one at random
catIds = coco.getCatIds(catNms=['table']);
imgIds = coco.getImgIds(catIds=catIds );
imgIds = coco.getImgIds(imgIds = [1])
img = coco.loadImgs(imgIds[np.random.randint(0,len(imgIds))])[0]

3.关键点标注可视化

import cv2

I = cv2.imread('%s/train2017/%s'%(dataDir, img['file_name']))

plt.axis('off')
plt.imshow(I)
plt.show()

在这里插入图片描述

# initialize COCO api for person keypoints annotations

coco_kps=coco

# load and display keypoints annotations
plt.imshow(I); plt.axis('off')
ax = plt.gca()
annIds = coco_kps.getAnnIds(imgIds=img['id'], catIds=catIds, iscrowd=None)
anns = coco_kps.loadAnns(annIds)
coco_kps.showAnns(anns)

在这里插入图片描述

六、思考总结

  • 转数据集是个繁琐的事情
  • 分解标注是最好的办法
  • 细节要注意到位(期间掉了好几个以为不重要的,结果就出错)

此文章为搬运
原项目链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值