COCO数据集解析

news2024/11/18 13:41:22

介绍

官网:https://cocodataset.org/
下载地址:https://cocodataset.org/#download
COCO的全称是Common Objects in COntext,起源于微软于2014年出资标注党的Microsoft COCO数据,与ImageNet竞赛一样,是计算机视觉领域最受关注和权威的比赛之一。
COCO数据是一个大型、丰富的目标检测、分割和字幕数据集。其中一些样本如下所示。
image.png
COCO数据集主要包含有标注和无标注的数据:

  • 2014:训练集+验证集+测试集
  • 2015:测试集
  • 2017:训练集+验证集+测试集+未标注

image.png
如上图和下表所示,两个数据的图像数量分别如下,其中测试集都未提供标注文件:

COCO2014COCO2017
trainvaltesttrainvaltest
827834050440775118287500040670

COCO数据集格式

COCO数据集包含5种类型的标注,分别是:目标检测、实例分割、关键点检测、全景分割和图像标注,都对应一个json文件。每个json文件主要包含如下五个部分:

{
  "info": info,
  "licenses": [license],
  "images": [image],
  "annotations": [annotation],
  "categories": [category]
}

info部分

info部分包含数据集的一些基本信息。

"info": 
{
  "description": "COCO 2017 Dataset",
  "url": "http://cocodataset.org",
  "version": "1.0","year": 2017,
  "contributor": "COCO Consortium",
  "date_created": "2017/09/01"
}

licenses部分

licenses部分包含数据集需要遵循的一些许可。

{
	"url":"http:\/\/creativecommons.org\/licenses\/by-nc-sa\/2.0\/",
	"id":1,
	"name":"Attribution-NonCommercial-ShareAlike License"
}

images部分

images中描述了数据集的图像信息,主要包括文件名、图像宽高信息。

{
    "coco_url": "", 
    "date_captured": "", 
    "file_name": "000001.jpg", 		# 只需要包含文件名即可, 在MMDetection训练中需要指定图像的路径
    "flickr_url": "", 
    "id": 1, 											# image_id
    "license": 0, 
    "width": 416, 								# 图像宽高
    "height": 416
}

categories描述了数据集中的类别信息,annotations中包含的是数据集的物体信息。不同的任务对应的json文件中annotation和categories的形式不同,分别如下:

目标检测和实例分割

目标检测和实例分割任务的标注文件为instances_train2017.json、instances_val2017.json这两个文件。

annotations部分

如下所示,每个对象实例都包含一系列字段,包括对象的类别ID、所属图像ID,annotation ID,分段掩码,目标框信息。

{
    "id": int,
    "image_id": int,
    "category_id": int,
    "segmentation": RLE or [polygon],
    "area": float,
    "bbox": [x,y,width,height],
    "iscrowd": 0 or 1
}

其中:

  • bbox为每个对象提供一个包围框,[x,y]表示框的左上角
  • segmentation格式取决于这个实例是单个对象(即iscrowd=0,将使用polygons格式,以多边形顶点表示。注意,单个对象可能需要多个多边形表示,例如遮挡时。),还是一组对象(即iscrowd=1,将使用RLE格式,mask编码)。
  • area表示标注区域的面积。如果是矩形框,那就是高乘以宽;如果是polygon或者RLE,则是mask区域围成的面积。

对于实例分割任务,annotations部分既要包含bbox元素,也要包含segmentation要素。而在segmentation部分由于实例的形式不同,表示的方法也不同。

polygon格式

polygon格式如下,比较简单,这些数按照相邻的顺序两两组成一个点的xy坐标,包含n个数必定为偶数,表示n/2个点坐标。

{
  "segmentation": [[68.59,227.1,...]],
  "area": 1441.7063,				# 目标区域面积
  "iscrowd": 0,
  "image_id": 210520,
  "bbox": [68.59,200.56,50.24,47.77],		# [x, y, width, height]边界框坐标, 其中x,y为图像左上角点
  "category_id": 50,		# 类别ID
  "id": 2231047					# 对象ID
}
RLE格式

如果iscrowd=1,那么segmentation就是RLE格式(segmentation字段会含有counts和size数组),如下所示。COCO数据的RLE都uncompressed RLE格式(与之相对的是compact RLE)。RLE所占字节的大小和边界上的像素数量是正相关的。
segmentation部分中的counts和size数组共同组成了这幅图片中的分割mask。其中size表示这张图像的宽高,然后对于一副图像的一个segmentation而言,每一个像素点要么在分割的目标区域中,要么是背景。显然,如果该像素在目标区域中为1,在背景中为1。那么对于一个张240×320的图像来说,共有76800个像素点,这样表示的大小为76800个bit,但是这样写很浪费空间,RLE则直接用0或1的个数表示。
RLE,Run-Length Encoding,变动长度编码算法,是一种对于二值图像的编码方式,以不同码字来表示连续的黑、白像素数。RLE格式是一种更加高效的图像语义分割数据表示格式,其数据以一串RLE编码的方式进行存储,而不是以像素点的形式存储,有效减少了数据的体积。RLE格式分割标注文件即是使用RLE格式对物体分割区域进行标注的文件。
很多分割数据集为了节省空间,标注文件采用了RLE格式,比如COCO等。RLE格式带来的好处就是基于RLE去计算目标区域的面积以及两个目标之间的union和intersection交并集时效果很高。

{
  "segmentation": {"counts": [272,2,4,4,4,4,...],"size": [240,320]},
  "area": 18419,
  "iscrowd": 1,
  "image_id": 448263,
  "bbox": [1,0,276,122],
  "category_id": 1,
  "id": 900100448263
}

基于Python实现RLE格式分割标注文件的格式转换 - 海_纳百川 - 博客园

polygon转RLE

假设输入的多边形数据,可以通过如下方式将其转换为RLE格式,以便后续转换为mask

if isinstance(mask_ann, list):
    # polygon -- a single object might consist of multiple parts
    # we merge all parts into one mask rle code
    rles = maskUtils.frPyObjects(mask_ann, img_h, img_w)
    rle = maskUtils.merge(rles)
RLE转mask二值图像

对于RLE格式的数据,直接调用decode方法来生成mask,其中0表示背景,1表示前景。

def _poly2mask(mask_ann, img_h, img_w):
    """Private function to convert masks represented with polygon to
    bitmaps.

    Args:
        mask_ann (list | dict): Polygon mask annotation input.
        img_h (int): The height of output mask.
        img_w (int): The width of output mask.

    Returns:
        numpy.ndarray: The decode bitmap mask of shape (img_h, img_w).
    """

    if isinstance(mask_ann, list):
        # polygon -- a single object might consist of multiple parts
        # we merge all parts into one mask rle code
        rles = maskUtils.frPyObjects(mask_ann, img_h, img_w)
        rle = maskUtils.merge(rles)
    elif isinstance(mask_ann['counts'], list):
        # uncompressed RLE
        rle = maskUtils.frPyObjects(mask_ann, img_h, img_w)
    else:
        # rle
        rle = mask_ann
    mask = maskUtils.decode(rle)
    return mask

def save_mask(mask, filename):
    mask = mask * 255		
    im = Image.fromarray(mask)
    im.save(filename)

if __name__ == "__main__":
    dataDir='datasets/COCO/'
    dataType='val2017'
    annFile='{}/annotations/instances_{}.json'.format(dataDir,dataType)
    # 初始化标注数据的 COCO api 
    coco=COCO(annFile)

    imgIds = coco.getImgIds(imgIds = [549220])
    # 获取到image结构体信息
    image = coco.loadImgs(imgIds[np.random.randint(0,len(imgIds))])[0]
    I = Image.open('%s/%s/%s'%(dataDir,dataType,image['file_name']))  
    I.save(image['file_name'])
    # 获取到annotation结构体信息
    annIds = coco.getAnnIds(imgIds=image['id'], iscrowd=0)
    annotation = coco.loadAnns(annIds[0])[0]
    # 将多边形或者RLE数据转换为mask
    mask = _poly2mask(annotation["segmentation"], img_h=image["height"], img_w=image['width'])    
    
    print(mask.shape, mask.dtype)
    # 保存mask
    save_mask(mask, "plot.png")
mask二值图像转RLE

基于上面的mask,转换为对应的RLE,步骤如下:

  1. 找到轮廓
  2. 将每个轮廓转换为RLE格式
def contours_to_rle(contours,  image_width, image_height):
    # 创建空白掩码图像
    mask_image = np.zeros((image_height, image_width), dtype=np.uint8)

    # 将每个轮廓绘制在掩码图像上
    cv2.drawContours(mask_image, contours, -1, 255, -1)

    # 将二进制掩码图像转换为 RLE 格式
    rle_encoding = maskUtils.encode(np.asfortranarray(mask_image))

    rle_encoding['counts'] = rle_encoding['counts'].decode('utf-8')
    # 返回 RLE 格式的分割信息
    return rle_encoding
# mask为上述转换RLE得到的
contours, hierarchy = cv2.findContours(mask * 255, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = [c for c in contours if cv2.contourArea(c)>100]

for contour in contours:
    # 将mask转换为RLEs
    rles = contours_to_rle(contour, mask.shape[1], mask.shape[0])
    # 以下代码是为了验证生成的RLEs是否有问题
    mask = _poly2mask(annotation["segmentation"], img_h=image["height"], img_w=image['width'])
    # 通过对比生成的mask可以知道不存在问题
    save_mask(mask, "plot.png")

categories部分

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

{
    "id": int,
    "name": str,
    "supercategory": str,
}

从json标注文件中摘取两个category实例如下:

{
	"supercategory": "person",
	"id": 1,
	"name": "person"
},
{
	"supercategory": "vehicle",
	"id": 2,
	"name": "bicycle"
},

COCO2017数据集中,共有80个category。

object Keypoint类型的格式

person_keypoints_train2017.json和person_keypoints_val2017.json这两个文件是COCO2017数据中表示关键点的标注文件。在不同的JSON文件中info、licenses和images是共享的,不共享的是annotation和categories这两个字段。

annotations字段

这个annotation结构体中包含了目标检测和实例分割任务annotation结构体的所有字段,再加上2个额外的字段。结构如下:

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,
}

其中keypoints是一个长度为3×k的数组,其中k是category中keypoints的总数量,在COCO中,k=17。每个keypoint是一个长度为3的数组,第一个和第二个元素表示关键点的x和y坐标值,第三个元素是一个标注位v。当v=0时表示这个关键点没有标注,v=1时表示这个关键点标注了但是不可见(遮挡),v为2时表示这个关键点标注了且可见。
num_keypoints表示这个目标上被标注的关键点数量,其中比较小的目标上可能无法标注全部的关键点。示例如下:

{
	"segmentation": [[125.12,539.69,140.94,522.43...]],
	"num_keypoints": 10,
	"area": 47803.27955,
	"iscrowd": 0,
	"keypoints": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,142,309,1,177,320,2,191,398...],
	"image_id": 425226,"bbox": [73.35,206.02,300.58,372.5],"category_id": 1,
	"id": 183126
},
categories字段

COCO2017数据中categories字段中的元素数量为1,只有person一个。相比于检测和分割,keypoints的categories中新增了两个额外的元素。定义如下:

{
    "id": int,
    "name": str,
    "supercategory": str,
    "keypoints": [str],
    "skeleton": [edge]
}

其中keypoints元素是一个长度为k的数字,包含了每个关键点的名字;skeleton定义了哥哥关键点之间的的连接性(比如人的左手腕和左肘是连接的,但是左手腕和右手腕不是)。示例如下:

{
	"supercategory": "person",
	"id": 1,
	"name": "person",
	"keypoints": ["nose","left_eye","right_eye","left_ear","right_ear","left_shoulder","right_shoulder","left_elbow","right_elbow","left_wrist","right_wrist","left_hip","right_hip","left_knee","right_knee","left_ankle","right_ankle"],
	"skeleton": [[16,14],[14,12],[17,15],[15,13],[12,13],[6,12],[7,13],[6,7],[6,8],[7,9],[8,10],[9,11],[2,3],[1,2],[1,3],[2,4],[3,5],[4,6],[5,7]]
}

COCO数据集的标注格式

COCOAPI使用

coco安装

coco的安装方式非常简单,运行如下命令即可。

git clone https://github.com/pdollar/coco.git

cd coco/PythonAPI
# 如果使用的是 python2, 运行下面的命令:  
make -j8
# 如果使用的是 python3, 需要更改 Makefile:  
vi Makefile
# 将 Makefile 中的 python 改为 python3, 然后:
make -j8

coco使用

加载json文件,得到coco对象

  1. 导入相关的库
from pycocotools.coco import COCO
import numpy as np
import skimage.io as io
import matplotlib.pyplot as plt
import pylab
pylab.rcParams['figure.figsize'] = (8.0, 10.0)
  1. 构建coco对象

COCO是一个Microsoft COCO数据集的辅助类,用于读取和可视化标记文件。输入参数为JSON文件的路径

dataDir='/path/to/your/coco_data'
dataType='val2017'
annFile='{}/annotations/instances_{}.json'.format(dataDir,dataType)
# 初始化标注数据的 COCO api 
coco=COCO(annFile)

coco对象创建完成之后会输出如下信息:

loading annotations into memory...
Done (t=0.81s)
creating index...
index created!

COCO类包含如下的属性:

  • dataset:JSON文件加载之后读取的内容都包含在dataset属性中;
  • img_ann_map:表示从img ID到ann的映射关系,通过img ID得到图像中包含哪些ann;
  • cat_img_map:表示cat ID到img ID的映射关系,通过cat ID得到img ID,表示该图像中包含这些类别的数据;
  • anns,cats,imgs:通过ID能得到对应的annotation、category、image结构体内容;

提供如下的常见方法:

getCatIds(self, catNms=[], supNms=[], catIds=[])

基于给定的类别名,超类名或者类别ID来获取到类别ID列表。

getImgIds(self, imgIds=[], catIds=[])

基于给定的img ID或者cat ID来获取img ID列表。

def loadAnns(self, ids=[]):

基于给定的ann ID来获取对应的annotation结构体列表。

def loadCats(self, ids=[]):

基于给定的cat ID来获取category结构体列表。

def loadImgs(self, ids=[]):

基于给定的img ID来获取image结构体列表。

def showAnns(self, anns, draw_bbox=False):

显示标注信息。通过plt.gca()获取当前figure的axes(轴),并在该figure上渲染标注内容。

显示数据集中的类别名称和超类

# 获取COCO数据集所有的类别ID
class_ids = coco.getCatIds()
# 基于类别ID得到对应的category结构体内容
cats = coco.loadCats(class_ids)
# 分析category结构体中name属性
names = [cat['name'] for cat in cats]
print('COCO categories: \n{}\n'.format(' '.join(names)))

names = set([cat['supercategory'] for cat in cats])
print('COCO supercategories: \n{}'.format(' '.join(names)))

输出信息如下:

COCO categories: 
person bicycle car motorcycle airplane bus train truck boat traffic light fire hydrant stop sign parking meter bench bird cat dog horse sheep cow elephant bear zebra giraffe backpack umbrella handbag tie suitcase frisbee skis snowboard sports ball kite baseball bat baseball glove skateboard surfboard tennis racket bottle wine glass cup fork knife spoon bowl banana apple sandwich orange broccoli carrot hot dog pizza donut cake chair couch potted plant bed dining table toilet tv laptop mouse remote keyboard cell phone microwave oven toaster sink refrigerator book clock vase scissors teddy bear hair drier toothbrush

COCO supercategories: 
outdoor food indoor appliance sports person animal vehicle furniture accessory electronic kitchen

加载指定img-ID的图片并显示

下面是加载并显示指定image_id的例子。

# get all images containing given categories, select one at random
catIds = coco.getCatIds(catNms=['person','dog','skateboard'])
# 获取catIds对应的所有image_id
imgIds = coco.getImgIds(catIds=catIds )
# 输出[549220, 324158, 279278]
# 指定image_id
imgIds = coco.getImgIds(imgIds = [549220])
# loadImgs() 返回的是只有一个元素的列表, 使用[0]来访问这个元素
# 列表中的这个元素又是字典类型, 关键字有: ["license", "file_name", "coco_url", "height", "width", "date_captured", "id"]
img = coco.loadImgs(imgIds[np.random.randint(0,len(imgIds))])[0]

# 加载并显示图片,可以使用两种方式: 1) 加载本地图片, 2) 在线加载远程图片
# 1) 使用本地路径, 对应关键字 "file_name"
I = Image.open('%s/%s/%s'%(dataDir,dataType,img['file_name']))  

# 2) 使用 url, 对应关键字 "coco_url"
# I = io.imread(img['coco_url'])    
plt.axis('off')
plt.imshow(I)
plt.savefig("plot.png", bbox_inches='tight',pad_inches=0.0)

下面就是指定image_id对应的图像数据:
#pic_centerimage.png

加载segmentation标注信息并显示在图片上

下面这段代码的作用就是加载segmentation标注信息,并将其显示在图片上。

# 加载并显示标注信息
plt.imshow(I); plt.axis('off')
annIds = coco.getAnnIds(imgIds=img['id'], catIds=catIds, iscrowd=None)
anns = coco.loadAnns(annIds)
coco.showAnns(anns)
plt.savefig("plot.png", bbox_inches='tight',pad_inches=0.0)

输出效果如下:
image.png
在showAnns函数中会调用plt.gca()函数来获取当前“figure”对象,如果存在的话直接返回,不存在的话会新建一个”figure“对象,并将其轴返回。
(一) COCO Python API - 使用篇-CSDN博客

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

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

相关文章

通讯协议学习之路:RS422协议理论

通讯协议之路主要分为两部分,第一部分从理论上面讲解各类协议的通讯原理以及通讯格式,第二部分从具体运用上讲解各类通讯协议的具体应用方法。 后续文章会同时发表在个人博客(jason1016.club)、CSDN;视频会发布在bilibili(UID:399951374) 一、…

京东店铺公司名爬虫

内容仅供学习参考,如有侵权联系删除 先通过京东非自营的店铺名拿到的公司名,再通过公司名称去其他平台拿到联系方式(代码省略) from aioscrapy.spiders import Spider from aioscrapy.http import Request, FormRequest import dd…

【LeetCode75】第七十四题 每日温度

目录 题目: 示例: 分析: 代码: 题目: 示例: 分析: 题目给我们一个数组,表示每天的温度,要我们返回一个同样长度的数组,答案里装着当前气温的下一个更高气…

Linux用户管理与远程管理

一、用户管理 1、回顾用户组管理 groupadd :组添加 groupmod:组修改 groupdel:组删除 与用户组相关的文件/etc/group,一共拥有4列 itheima:x:1000: 1-组名称 2-组密码 3-组编号 4-组内用户信息(这个组必须是这个…

阿里云优惠口令(2023更新)

2023年阿里云域名优惠口令,com域名续费优惠口令“com批量注册更享优惠”,cn域名续费优惠口令“cn注册多个价格更优”,cn域名注册优惠口令“互联网上的中国标识”,阿里云优惠口令是域名专属的优惠码,可用于域名注册、续…

微信公众号粉丝迁移步骤?

公众号迁移有什么作用?只能变更主体吗?公众号迁移是将原公众号的粉丝、违规记录、文章和素材库(可选)迁移至一个新的公众号。整体流程较为复杂,需花费7-10天。通过公众号迁移功能可以将A账号的粉丝、文章素材&#xff…

ROS工具RViz可视化3D空间的一些错误的解决方案

1、引言 RViz是ROS的一个可视化3D空间的工具,它用于帮助用户在ROS系统中导航、查看和交互。RViz可以显示来自各种ROS节点的信息,例如传感器数据、机器人的状态和路径等。通过RViz,用户可以以图形方式查看和交互各种ROS数据,非常直…

连锁药店的自有品牌之争:老百姓大药房能否突围?

文丨新熔财经 作者丨楷楷 近年来,随着医保谈判药品的“双通道”(即消费者可在有资质的药店买到新进医保的创新药),以及“门诊统筹”将药店纳入医保报销等医改政策出台,药企开始重新重视起零售药店渠道,很…

C++对象模型(18)-- 函数语义学:函数调用过程

1、栈帧结构 函数执行是通过系统栈来实现的,系统栈分为若干个栈帧。 栈帧就是函数运行的环境,每个函数在被调用时都会在系统栈区形成一个叫栈帧的结构。一次函数调用相关的数据保存在栈帧中,比如函数参数、函数的局部变量、函数执行完后的返…

Leetcode 21 合并两个有序链表 (链表)

Leetcode 21 合并两个有序链表 (链表) 解法1 复杂的第一版本(优化大于和等于合并见方法二)解法2 注意注意:先添加元素toadd.next list1,添加之后才可以移动指针toadd toadd.next 解法1 复杂的第一版本&a…

数字电路常用芯片合集

前言 本文归纳了本科数字电路中常见的芯片型号及其功能,分为以下几类: 组合逻辑电路芯片 时序逻辑电路芯片 D/A A/D相关芯片 组合逻辑电路芯片 优先编码器74HC148 功能:多→1,选通 逻辑框图 功能表 补充:可以…

react native 使用夜神模拟器开发调试 windows+android

执行adb devices, 提示List of devices attached 打开本地sdk目录中的platform-tools文件夹,复制下面3个文件 打开夜神模拟器安装目录中的bin目录,把复制出来的文件复制替换到bin目录中 在复制一份platform-tools目录中的adb.exe,重命名为…

java最新Springboot3+微服务实战12306高性能售票系统全套开发课程

java最新Springboot3微服务实战12306高性能售票系统全套开发课程 视频课程在文末获取 第1章 课程介绍与学习指南。 1-1 课前必读(不读错过一个亿) 1-2 课程导学 1-3 为什么要选择最新版本SpringBoot3和JDK17? 1-4 在线demo网站演示 第2…

现货黄金的走势怎么看

成功的现货黄金交易者,都有自己一套的看行情的方法,以及自己最熟悉的交易获利机会,这使他们获利的胜率能够保持在一定的水平之上,长远来说,这也使他们成为市场上的赢家。 现货黄金的价格走势总在不断的变化之中&#x…

GEE:绘制土地利用类型面积分布柱状图

作者:CSDN @ _养乐多_ 本文记录了,在 Google Earth Engine (GEE)中进行随机森林分类后绘制不同类型面积分布柱状图的代码片段。 完整代码请看博客《GEE:随机森林分类教程(样本制作、特征添加、训练、精度、参数优化、贡献度、统计面积)》 柱状图效果如下所示, 文章目…

Pulsar-Pulsar 之 Functions

Pulsar 之 Functions Pulsar FunctionsPulsar Functions是什么?为什么使用Pulsar Functions?实例 完全限定函数名函数实例函数workerFunctions 运行时处理保证和订阅类型上下文函数消息类型Window functionwindow的类型快速开始启用有状态的函数使用Puls…

通讯协议学习之路:RS485协议理论

通讯协议之路主要分为两部分,第一部分从理论上面讲解各类协议的通讯原理以及通讯格式,第二部分从具体运用上讲解各类通讯协议的具体应用方法。 后续文章会同时发表在个人博客(jason1016.club)、CSDN;视频会发布在bilibili(UID:399951374) 序&…

接口测试vs功能测试

接口测试和功能测试的区别: 本文主要分为两个部分: 第一部分:主要从问题出发,引入接口测试的相关内容并与前端测试进行简单对比,总结两者之前的区别与联系。但该部分只交代了怎么做和如何做?并没有解释为什…

“一键替换视频封面,高效批量剪辑从未如此简单

在一个充满竞争和创意的世界,吸引人的视频封面往往能够决定内容的成败。今天,我将向大家介绍一种可以轻松替换视频封面图片的实用技巧,让你在创作中更胜一筹! 首先,我们要进入媒体梦工厂主页面,并点击“视…

Go语言入门心法(十一): 文件处理

Go语言入门心法(一): 基础语法 Go语言入门心法(二): 结构体 Go语言入门心法(三): 接口 Go语言入门心法(四): 异常体系 Go语言入门心法(五): 函数 Go语言入门心法(六): HTTP面向客户端|服务端编程 Go语言入门心法(八): mysql驱动安装报错onnection failed Go语言入门心法(…