ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

FAIR开源框架detectron2代码解析

2020-12-02 13:58:15  阅读:455  来源: 互联网

标签:parser FAIR -- cfg detectron2 args 开源 demo image


detectron2框架解析


(大概率要tj的博客)

0. 简介

   detectron2是Facebook的下一代开源框架,基于pytorch,是fast rcnn benchmark的下一任继承者,FAIR基于此实现了大量的目标检测的SOTA算法,并且现在仍然在更新。官方给出的model zoo里同样包含了很多的算法,这是开箱即用的。此外也有大量的新的算法是基于detectron2实现的,例如CenterMask,Sparse RCNN等。
   框架已经开源,地址在这里: detectron2的地址
   具体的安装过程参考官方即可,依赖的东西很少,不再赘述。

1. 从demo开始管中窥豹

1.1 开始使用

   在安装好以后,官方提供了两种方式使用detectron2,第一种是API接口的方式,具体使用可以参考官方教程里colab上的步骤;第二种方法是在命令行内使用demo.py进行预测,本文主要从第二种方式入手,分析detectron2的调用过程,以此来简要解析代码框架。
  官方给出的调用demo.py的命令为

cd demo/

python demo.py --config-file ../configs/COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x.yaml \
  --input input1.jpg input2.jpg \
  [--other-options]
  --opts MODEL.WEIGHTS detectron2://COCO-InstanceSegmentation/mask_rcnn_R_50_FPN_3x/137849600/model_final_f10217.pkl

  参数--config后面跟着的是网络的配置文件,也就是存储网络结构的文件存放的位置,--input指定输入图片,[--other-options]是其他选项的意思,这里只是示意,实际的使用是参考后面的格式,--opts指定了选项,这里指定了权重的位置,实际上如果不指定的话,demo会在远处下载。
  在使用的时候,我遇到了线程问题,不知道是cuda的原因还是torch的问题(个人倾向于框架的代码原因)。

1.2 demo.py代码解析

1.2.1 结构解析

demo.py的大致结构如下:

# 有删减,还有一些其余包的导入
from detectron2.config import get_cfg
from detectron2.data.detection_utils import read_image
from detectron2.utils.logger import setup_logger

from predictor import VisualizationDemo

# constants
WINDOW_NAME = "COCO detections"

def setup_cfg(args):
    cfg = get_cfg()
    #有删减
    return cfg
 
 def get_parser():
    parser = argparse.ArgumentParser(description="Detectron2 demo for builtin configs")
    #有删减
    return parser

if __name__ == "__main__":
    #有删减
    args = get_parser().parse_args()
    #···
    cfg = setup_cfg(args)
    demo = VisualizationDemo(cfg)
    #···
    if args.input:
        #···
    elif args.webcam:
        #···
    elif args.video_input:
        #···

  首先我们从main函数入手,在这个模块被当作主函数调用的时候,它的简要步骤如下:

Created with Raphaël 2.2.0 开始 初始化相关配置 参数是否有--input 执行与--input相关的代码 结束 参数是否有--webcam 执行与--webcam相关的代码 参数是否有--video_input 执行与--video_input相关的代码 yes no yes no yes no

可以看到,代码首先执行了一些加载过程,紧接着判断输入的类型来决定接下来如何处理。因此我们先研究加载过程。

1.2.2 加载过程解析

首先加载部分的代码如下:

import multiprocessing as mp

if __name__ == "__main__":
    mp.set_start_method("spawn", force=True)
    args = get_parser().parse_args()
    setup_logger(name="fvcore")
    logger = setup_logger()
    logger.info("Arguments: " + str(args))

    cfg = setup_cfg(args)

    demo = VisualizationDemo(cfg)

  可以看到,这部分先是启动了一个新的线程,关于set_start_method这部分,我不是很懂,但是我猜测可能与直接运行demo出错有关系。这部分的官方文档的解释如下:

spawn
The parent process starts a fresh python interpreter process. The child process will only inherit those resources necessary to run the process object’s run() method. In particular, unnecessary file descriptors and handles from the parent process will not be inherited. Starting a process using this method is rather slow compared to using fork or forkserver.
Available on Unix and Windows. The default on Windows and macOS.

1.2.2.1 命令行参数加载

  紧接着,代码调用了get_parser().parse_args()并把返回值赋给args
  我们先看一下get_parser()函数的完整定义如下:

import argparse

def get_parser():
    parser = argparse.ArgumentParser(description="Detectron2 demo for builtin configs")
    parser.add_argument(
        "--config-file",
        default="configs/quick_schedules/mask_rcnn_R_50_FPN_inference_acc_test.yaml",
        metavar="FILE",
        help="path to config file",
    )
    parser.add_argument("--webcam", action="store_true", help="Take inputs from webcam.")
    parser.add_argument("--video-input", help="Path to video file.")
    parser.add_argument(
        "--input",
        nargs="+",
        help="A list of space separated input images; "
        "or a single glob pattern such as 'directory/*.jpg'",
    )
    parser.add_argument(
        "--output",
        help="A file or directory to save output visualizations. "
        "If not given, will show output in an OpenCV window.",
    )

    parser.add_argument(
        "--confidence-threshold",
        type=float,
        default=0.5,
        help="Minimum score for instance predictions to be shown",
    )
    parser.add_argument(
        "--opts",
        help="Modify config options using the command-line 'KEY VALUE' pairs",
        default=[],
        nargs=argparse.REMAINDER,
    )
    return parser

argparse库的作用就是解析输入命令行的参数,每一个的add_argument都指定了一个可接受的参数,否则不予接受,然后返回一个接受了所有参数的ArgumentParser对象。在这里可以看到很多的细节,比如config文件的默认路径。
  在被解析了以后,整个的参数列表就会被返回,而那些没有被设置的又是必须的参数会被赋予默认值
  其中,parser是一个argparse.ArgumentParser的类,而get_parser().parse_args()调用的就是这个对象的所有名字空间(Namespace),其中所有的参数都可以像函数那样调用他们,例如get_parser().parse_args().config-file或者args.config-file就是配置文件的路径。

1.2.2.2 logger加载

  然后代码调用了detectron2.utils.logger.setup_logger(name="fvcore") (注意有导入from detectron2.utils.logger import setup_logger)来开启一个detectron2的记录器,并设置为DEBUGG级别(这是官网翻译过来的),具体是干什么的说实话细节我也不清楚,但是可以认为是然并卵的东西(可能有助于发现错误,自信一点的可以忽略)。接下来的几行都是处理logger相关的函数。

1.2.2.3 模型配置加载

  在加载完logger以后,调用cfg设置函数cfg = setup_cfg(args),根据我们刚刚加载好的命令行参数args设置了一系列的东西。 setup_cfg函数的定义如下:

from detectron2.config import get_cfg

def setup_cfg(args):
    # load config from file and command-line arguments
    cfg = get_cfg()
    # To use demo for Panoptic-DeepLab, please uncomment the following two lines.
    # from detectron2.projects.panoptic_deeplab import add_panoptic_deeplab_config  # noqa
    # add_panoptic_deeplab_config(cfg)
    cfg.merge_from_file(args.config_file)
    cfg.merge_from_list(args.opts)
    # Set score_threshold for builtin models
    cfg.MODEL.RETINANET.SCORE_THRESH_TEST = args.confidence_threshold
    cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = args.confidence_threshold
    cfg.MODEL.PANOPTIC_FPN.COMBINE.INSTANCES_CONFIDENCE_THRESH = args.confidence_threshold
    cfg.freeze()
    return cfg

  get_cfg函数负责返回detectron2的默认参数的一份拷贝,而这份参数是以CfgNode进行存储的,包含了大量的网络信息,但是要注意的是缺少了例如权重路径之类的关键信息,因此需要进行设置。
  而merge_from_file函数则是CfgNode的类方法,他会进行参数更新(没有细看)
  然后设置了三个阈值,其中第一个阈值cfg.MODEL.RETINANET.SCORE_THRESH_TEST预测类概率的阈值,只有当预测出的bbox的概率,也就是score大于这个阈值的时候才会被认为这是一个预测,默认的阈值为0.05
  第二个阈值cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST是用来平衡精度与召回率的,这一段的解释比较长,官方的注释如下:

Minimum score threshold (assuming scores in a [0, 1] range); a value chosen to balance obtaining high recall with not having too many low precision detections that will slow down inference post processing steps (like NMS). A default threshold of 0.0 increases AP by ~0.2-0.3 but significantly slows down inference.

  第三个参数cfg.MODEL.PANOPTIC_FPN.COMBINE.INSTANCES_CONFIDENCE_THRESH是在做实例分割任务的时候使用的参数,官方没有给出具体的解释。
总之,参数不止这几个,具体的参数可以参看官方文档:参数解释

最终调用了freeze()函数,使所有的参数不可变,然后返回这个设置好的固定参数组。

1.2.2.4 模型生成

  在初始化模型参数完成后VisualizationDemo被调用,VisualizationDemo函数是从demo.py旁边的predictor.py导入进来的,它的代码如下:

#有删减
from detectron2.data import MetadataCatalog
from detectron2.engine.defaults import DefaultPredictor
class VisualizationDemo(object):
    def __init__(self, cfg, instance_mode=ColorMode.IMAGE, parallel=False):
        """
        Args:
            cfg (CfgNode):
            instance_mode (ColorMode):
            parallel (bool): whether to run the model in different processes from visualization.
                Useful since the visualization logic can be slow.
        """
        self.metadata = MetadataCatalog.get(
            cfg.DATASETS.TEST[0] if len(cfg.DATASETS.TEST) else "__unused"
        )
        self.cpu_device = torch.device("cpu")
        self.instance_mode = instance_mode

        self.parallel = parallel
        if parallel:
            num_gpu = torch.cuda.device_count()
            self.predictor = AsyncPredictor(cfg, num_gpus=num_gpu)
        else:
            self.predictor = DefaultPredictor(cfg)

    def run_on_image(self, image):
        """
        Args:
            image (np.ndarray): an image of shape (H, W, C) (in BGR order).
                This is the format used by OpenCV.

        Returns:
            predictions (dict): the output of the model.
            vis_output (VisImage): the visualized image output.
        """
        

    def _frame_from_video(self, video):
       #···

    def run_on_video(self, video):
        """
        Visualizes predictions on frames of the input video.

        Args:
            video (cv2.VideoCapture): a :class:`VideoCapture` object, whose source can be
                either a webcam or a video file.

        Yields:
            ndarray: BGR visualizations of each video frame.
        """
        

class AsyncPredictor:
    #···

  在这部分,最关键的是parallel参数的选择,如果设置了并行化,则会在gpu上异步执行代码,否则会生成一个DefaultPredictor的类并返回,而这个异步类的定义也在这个文件夹下。本文以更通用的DefaultPredictor类作为解析。
  DefaultPredictor是定义在detectron2.engine下的,这是通过使用传入的cfg参数来构建一个使用单设备,对单图片进行预测的predictor

Bases: object
Create a simple end-to-end predictor with the given config that runs on single device for a single input image.
Compared to using the model directly, this class does the following additions:
(1): Load checkpoint from cfg.MODEL.WEIGHTS.
(2): Always take BGR image as the input and apply conversion defined by cfg.INPUT.FORMAT.
(3): Apply resizing defined by cfg.INPUT.{MIN,MAX}_SIZE_TEST.
(4): Take one input image and produce a single output, instead of a batch.
If you’d like to do anything more fancy, please refer to its source code as examples to build and use the model manually.

而这个predictor是可以直接用来生成预测的,官方给出的例子代码如下:

pred = DefaultPredictor(cfg)
inputs = cv2.imread("input.jpg")
outputs = pred(inputs)

通常来说,是可以像这样直接使用detectron2的API接口的,并且基本上是可行的(我一般是这么干的)。

1.2.2.5 总结

  加载部分首先读取命令行给出的参数,读取关键的权重以及网络配置信息,并得到一个参数对象;紧接着生成一个logger;接下来将命令行参数与大量的默认参数进行合并,生成最终的网络配置;最后,判断硬件环境选择GPU还是在CPU,然后在硬件上根据配置文件生成并初始化检测器对象并返回。
  这样就得到了这个检测器对象,如果我们已知了输入的类型,实际上可以直接调用这个对象生成预测,就像上面的例子代码那样。

1.2.3 图片处理代码解析

  我们在使用的时候的输入为input标签,也就是图片,而webcomvideo都是视频,在这里我个人没有做仔细研究,如果你有兴趣也可以研究一下。
  而input标签下对应的代码如下:

if args.input:
        if len(args.input) == 1:
            args.input = glob.glob(os.path.expanduser(args.input[0]))
            assert args.input, "The input path(s) was not found"
        for path in tqdm.tqdm(args.input, disable=not args.output):
            # use PIL, to be consistent with evaluation
            img = read_image(path, format="BGR")
            start_time = time.time()
            predictions, visualized_output = demo.run_on_image(img)
            logger.info(
                "{}: {} in {:.2f}s".format(
                    path,
                    "detected {} instances".format(len(predictions["instances"]))
                    if "instances" in predictions
                    else "finished",
                    time.time() - start_time,
                )
            )

            if args.output:
                if os.path.isdir(args.output):
                    assert os.path.isdir(args.output), args.output
                    out_filename = os.path.join(args.output, os.path.basename(path))
                else:
                    assert len(args.input) == 1, "Please specify a directory with args.output"
                    out_filename = args.output
                visualized_output.save(out_filename)
            else:
                cv2.namedWindow(WINDOW_NAME, cv2.WINDOW_NORMAL)
                cv2.imshow(WINDOW_NAME, visualized_output.get_image()[:, :, ::-1])
                if cv2.waitKey(0) == 27:
                    break  # esc to quit
    elif args.webcam:
        #···
    elif args.video_input:
        #···

这部分相当于只有一个大块的for循环,它的内部主要干了以下的四件事情:

  • 读取图片
  • 做预测
  • 输出信息
  • 可视化/保存图片

1.2.3.1 读取函数

detectron2.data.detection_utils.read_image 函数的实现代码如下:

def read_image(file_name, format=None):
    """
    Read an image into the given format.
    Will apply rotation and flipping if the image has such exif information.

    Args:
        file_name (str): image file path
        format (str): one of the supported image modes in PIL, or "BGR" or "YUV-BT.601".

    Returns:
        image (np.ndarray): an HWC image in the given format, which is 0-255, uint8 for
            supported image modes in PIL or "BGR"; float (0-1 for Y) for YUV-BT.601.
    """
    with PathManager.open(file_name, "rb") as f:
        image = Image.open(f)

        # work around this bug: https://github.com/python-pillow/Pillow/issues/3973
        image = _apply_exif_orientation(image)
        return convert_PIL_to_numpy(image, format)

很明显可以发现,这是用PIL库读取图片,并把图片转换为指定格式(RGB或者YUV)的np.ndarray,并返回这个数组。

1.2.3.2 执行预测

  predictions, visualized_output = demo.run_on_image(img)这部分的代码使用我们之前生成好的预测器对象demo然后调用它的run_on_image函数,对刚刚读取好的图像数组进行预测。
  demo是属于predictor.py下的VisualizationDemo类的一个实例化对象,类中定义的函数的具体代码如下:

def run_on_image(self, image):
        """
        Args:
            image (np.ndarray): an image of shape (H, W, C) (in BGR order).
                This is the format used by OpenCV.

        Returns:
            predictions (dict): the output of the model.
            vis_output (VisImage): the visualized image output.
        """
        vis_output = None
        predictions = self.predictor(image)
        # Convert image from OpenCV BGR format to Matplotlib RGB format.
        image = image[:, :, ::-1]
        visualizer = Visualizer(image, self.metadata, instance_mode=self.instance_mode)
        if "panoptic_seg" in predictions:
            panoptic_seg, segments_info = predictions["panoptic_seg"]
            vis_output = visualizer.draw_panoptic_seg_predictions(
                panoptic_seg.to(self.cpu_device), segments_info
            )
        else:
            if "sem_seg" in predictions:
                vis_output = visualizer.draw_sem_seg(
                    predictions["sem_seg"].argmax(dim=0).to(self.cpu_device)
                )
            if "instances" in predictions:
                instances = predictions["instances"].to(self.cpu_device)
                vis_output = visualizer.draw_instance_predictions(predictions=instances)

        return predictions, vis_output

从注释里很明显可以读到,predictions是预测的结果,而vis_output很显然是可视化的结果。也就是说,如果我们直接要拿数据进行处理的话,那么就应该从predictions入手。
从代码里可以看到的一点是,对读入的image是进行了一个格式转换的,从注释里可以看到:

Convert image from OpenCV BGR format to Matplotlib RGB format.

紧接着,predictor被调用用来生成predictions,请注意,这里的predictor实际上就是前面的例子代码里的部分。

1.2.3.3 记录结果与可视化

这一块的重点就没有那么多了,我没有仔细看,如果你需要进行可视化的展示,还是请自行阅读。

1.2.3.4 总结

  这一部分首先判断输入格式,在符合输入格式的情况下,然后读取图片,紧接着使用在上一步生成好的检测器对象来对图片进行预测,接下来记录结果并选择性的进行可视化展示。

1.3 从demo迁移到自己的功能

1.3.1 功能简介

  我现在需要利用某个模型对某些图片进行批量化的预测,并把返回的结果存储在一个json格式的文件里,为了方便以后使用和可视化,我需要让这个json文件符合cocoapi的格式。

1.3.2 功能分析

  首先我们需要简单的重写demo里的解析参数的函数,因为我们要用的是一批次的文件,而不是一个,假设我们的文件都放在某个文件夹下,而其余的参数似乎不需要什么修改。
  其次,我们在生成了模型以后,应该反复调用这个对象,对我们的图片进行读取。
  最后我们需要将这些输出预测转换到cocoapi符合的格式。

1.3.3 功能实现

1.3.3.1 命令行解析函数改写

其实我们的函数足够简单,删减并改写后的如下:

#只需要对默认函数增加下面的代码即可
def my_get_parser():
    #changed,其实也可设置default,我们就不用手动输入了
    parser.add_argument(
        "--inputdir",
        help="A list of space separated input images; "
        "or a single glob pattern such as 'directory/*.jpg'",
    )

1.3.3.2 实现多次预测

最终,我们列出文件夹下的所有图片,逐次调用,在这里我们使用os.listdir函数来进行列出,然后与文件夹名称进行拼接,逐个打开,然后输入进预测器。
也即:

for pic in os.listdir(dirname):
	path = dirname+"/"+pic
	img = read_img(path)
	outputs = predictor(img)

1.3.3.3 对输出进行处理

我们在利用predictor得到了结果以后,并不能直接拼接,因此此时结果还在gpu上,同时,为了转换成cocoapi的格式,我们需要进行一定的处理。
cocoapi在目标检测任务里的标签格式如下:

[{'image_id':368,'category_id':1,'bbox':[1,1,10,10],'score':0.69},{'image_id':368,'category_id':1,'bbox':[1,1,10,10],'score':0.69}]

其中bbox是以[x,y,w,h]的格式存放的,而FCOS里是按照[x1,y1,x2,y2]的格式存放的,因此需要转换。此外FCOS预测出的类别也需要转换,FCOS预测出的类别是从0到80的连续数字,而实际上cocapi使用的则是从1到90挑选出的80类。
最终,我们的转换代码示意如下:

def transform_output(outputs):
	out = outputs.to_cpu()
	bbox = transform_bbox(out['bbox'])
	id = transform_id(out['id'])
	return new_out

1.3.3.4 代码展示

在细节上实现了我们之前提到的东西的最终代码如下:

# Copyright (c) Facebook, Inc. and its affiliates.
import argparse
import glob
import multiprocessing as mp
import os
import time
import cv2
import tqdm

#在这里我的任务是使用FCOS,FCOS的配置函数做了一点改变
#from detectron2.config import get_cfg
from adet.config import get_cfg

from detectron2.data.detection_utils import read_image
from detectron2.utils.logger import setup_logger

from predictor import VisualizationDemo

# constants
WINDOW_NAME = "COCO detections"

#导入包
import re
import json


def setup_cfg(args):
    # load config from file and command-line arguments
    cfg = get_cfg()
    # To use demo for Panoptic-DeepLab, please uncomment the following two lines.
    # from detectron2.projects.panoptic_deeplab import add_panoptic_deeplab_config  # noqa
    # add_panoptic_deeplab_config(cfg)
    cfg.merge_from_file(args.config_file)
    cfg.merge_from_list(args.opts)
    # Set score_threshold for builtin models
    cfg.MODEL.RETINANET.SCORE_THRESH_TEST = args.confidence_threshold
    cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = args.confidence_threshold
    cfg.MODEL.PANOPTIC_FPN.COMBINE.INSTANCES_CONFIDENCE_THRESH = args.confidence_threshold
    cfg.freeze()
    return cfg

#因为FCOS的预测类是0~80而真实的cocoapi需要的是转换以后的。
def getCocoIds():
    return [ 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,43,44,46,47,48,49,50,
            51,52,53,54,55,56,57,58,59,60,
            61,62,63,64,65,67,70,
            72,73,74,75,76,77,78,79,80,
            81,82,84,85,86,87,88,89,90]


#处理一个图片的预测类的函数
def getOnePicBBox(outputs,cocoids,img_id):
    pred_list = []
    #这里需要转换到cpu上进行操作
    for out_tsr,out_label,score in zip(outputs['instances'].to("cpu").pred_boxes.tensor,outputs['instances'].pred_classes.to("cpu").numpy().tolist(),outputs['instances'].scores.to('cpu').numpy().tolist()):
        pred_dict = {}
        #bbox=[x1,y1,x2,y2]
        bbox = out_tsr.numpy().tolist()
        bbox = [bbox[0],bbox[1],bbox[2]-bbox[0],bbox[3]-bbox[1]]
        cat_id = cocoids[out_label]
        #print(bbox)
        pred_dict['image_id'] = img_id
        pred_dict['category_id'] = cat_id
        pred_dict['bbox'] = bbox
        pred_dict['score'] = score
        pred_list.append(pred_dict)
    
    return pred_list

#定义自己的解析函数
def my_get_parser():
    parser = argparse.ArgumentParser(description="Detectron2 demo for builtin configs")
    parser.add_argument(
        "--config-file",
        default="configs/quick_schedules/mask_rcnn_R_50_FPN_inference_acc_test.yaml",
        metavar="FILE",
        help="path to config file",
    )
    #changed,其实也可设置default,我们就不用手动输入了
    parser.add_argument(
        "--inputdir",
        help="A list of space separated input images; "
        "or a single glob pattern such as 'directory/*.jpg'",
    )
    
    parser.add_argument(
        "--confidence-threshold",
        type=float,
        default=0.5,
        help="Minimum score for instance predictions to be shown",
    )
    parser.add_argument(
        "--opts",
        help="Modify config options using the command-line 'KEY VALUE' pairs",
        default=[],
        nargs=argparse.REMAINDER,
    )
    return parser

if __name__ == "__main__":
    #mp.set_start_method("spawn", force=True)
    args = my_get_parser().parse_args()
    setup_logger(name="fvcore")
    logger = setup_logger()
    logger.info("Arguments: " + str(args))

    cfg = setup_cfg(args)

    demo = VisualizationDemo(cfg)
    pred_list = []
    cocoids = getCocoIds()
    index = 0
    for pic in os.listdir(args.inputdir):
        path = args.inputdir + "/" + pic
        img = read_image(path, format="BGR")
        img_id = int(re.split(r'\.',pic)[0])        
        if len(img.shape) < 3:
            print(img_id," continue! ",index)
            index += 1
            continue
        
        outputs = demo.predictor(img)
        print(img_id," predicted! ",index)
        index += 1
        pred_list += getOnePicBBox(outputs,cocoids,img_id)
    
    print(len(pred_list))
    jsObj = json.dumps(pred_list)
    fileObject = open('allPredList.json', 'w')
    fileObject.write(jsObj)
    fileObject.close()
    print("to json end!")
        

最后,我们在命令行调用我们的my_demo.py就可以了:python demo/my_demo.py --config-file configs/FCOS-Detection/R_50_1x.yaml --inputdir data/val2017 --opts MODEL.WEIGHTS configs/FCOS-Detection/FCOS_R_50_1x.pth

1.4 总结

总的来说,detectron2的使用相对来说很方便,即成体系又有大量的算法遵循这套框架,速度上来说也还过得去,相对来说算比较快的。

后记

这部分是因为自己实验要用到所以作了这个很简单的解析,后续的话如果还要继续使用,那么我会继续尝试记录自己学到的东西,如果用不到的话,那大概率不会了。

标签:parser,FAIR,--,cfg,detectron2,args,开源,demo,image
来源: https://blog.csdn.net/qq_36868451/article/details/110446835

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有