13 | Visual feature pyramid has shown its superiority in both effectiveness and efficiency in a wide range of applications. However, the existing methods exorbitantly concentrate on the inter-layer feature interactions but ignore the intra-layer feature regulations, which are empirically proved beneficial. Although some methods try to learn a compact intra-layer feature representation with the help of the attention mechanism or the vision transformer, they ignore the neglected corner regions that are important for dense prediction tasks. To address this problem, in this paper, we propose a Centralized Feature Pyramid (CFP) for object detection, which is based on a globally explicit centralized feature regulation. Specifically, we first propose a spatial explicit visual center scheme, where a lightweight MLP is used to capture the globally long-range dependencies and a parallel learnable visual center mechanism is used to capture the local corner regions of the input images. Based on this, we then propose a globally centralized regulation for the commonly-used feature pyramid in a top-down fashion, where the explicit visual center information obtained from the deepest intra-layer feature is used to regulate frontal shallow features. Compared to the existing feature pyramids, CFP not only has the ability to capture the global long-range dependencies, but also efficiently obtain an all-round yet discriminative feature representation. Experimental results on the challenging MS-COCO validate that our proposed CFP can achieve the consistent performance gains on the state-of-the-art YOLOv5 and YOLOX object detection baselines.
14 |
15 | ## The overall architecture
16 | 
17 |
18 | ## Qualitative results
19 | 
20 |
21 | ## Quantitative results and training weights
22 | We provide training weights of CFP with YOLOX and YOLOv5 as the baseline.
23 |
24 | | Model | size | mAP(%) | weights |
25 | | :--- | :---: | :---: | ---: |
26 | | CFP-s (YOLOX)| 640 | 41.10 | [weight](https://pan.baidu.com/disk/main#/index?category=all&path=%2FCFP-main%2Fweights) |
27 | | CFP-m (YOLOX)| 640 | 46.40 | [weight](https://pan.baidu.com/disk/main#/index?category=all&path=%2FCFP-main%2Fweights) |
28 | | CFP-l (YOLOX)| 640 | 49.40 | [weight](https://pan.baidu.com/disk/main#/index?category=all&path=%2FCFP-main%2Fweights) |
29 | | CFP-s (YOLOv5)| 640 | 36.00 | [weight](https://pan.baidu.com/disk/main#/index?category=all&path=%2FCFP-main%2Fweights) |
30 | | CFP-m (YOLOv5)| 640 | 43.20 | [weight](https://pan.baidu.com/disk/main#/index?category=all&path=%2FCFP-main%2Fweights) |
31 | | CFP-l (YOLOv5)| 640 | 46.60 | [weight](https://pan.baidu.com/disk/main#/index?category=all&path=%2FCFP-main%2Fweights) |
32 |
33 | ## Installation
34 | #### - Install CFP-main from source
35 | ```
36 | git clone git@github.com:QY1994-0919/CFP-main.git
37 | cd CFP-main
38 | pip3 install -v -e . # or python3 setup.py develop
39 | ```
40 |
41 | #### - Prepare COCO dataset
42 | ```
43 | cd CFP-main
44 | ln -s /path/to/your/COCO ./datasets/COCO
45 | ```
46 |
47 | ## Usage
48 | #### - To train the model, please run:
49 | ```
50 | python -m cfp.tools.train -f cfp-s -d 2 -b 16 --fp16 -o [--cache]
51 | python -m cfp.tools.train -f cfp-m -d 2 -b 16 --fp16 -o [--cache]
52 | python -m cfp.tools.train -f cfp-l -d 2 -b 16 --fp16 -o [--cache]
53 | ```
54 |
55 | #### - To test the model, please run:
56 | ```
57 | python -m cfp.tools.eval -n cfp-s -c cfp_s.pth -b 16 -d 2 --conf 0.001 [--fp16] [--fuse]
58 | python -m cfp.tools.eval -n cfp-m -c cfp_s.pth -b 16 -d 2 --conf 0.001 [--fp16] [--fuse]
59 | python -m cfp.tools.eval -n cfp-l -c cfp_s.pth -b 16 -d 2 --conf 0.001 [--fp16] [--fuse]
60 | ```
61 |
62 | ## Acknowledgement
63 | Thanks [YOLOv5](https://github.com/ultralytics/yolov5) and [YOLOX](https://arxiv.org/abs/2107.08430) teams for the wonderful open source project!
64 |
65 | ## Bibtex
66 | If you find this work is useful for your research, please cite our paper:
67 | ```
68 | @article{quan2022centralized,
69 | title={Centralized Feature Pyramid for Object Detection},
70 | author={Quan, Yu and Zhang, Dong and Zhang, Liyan and Tang, Jinhu},
71 | journal={arXiv},
72 | year={2022}}
73 | ```
74 |
--------------------------------------------------------------------------------
/cfp/core/launch.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Code are based on
4 | # https://github.com/facebookresearch/detectron2/blob/master/detectron2/engine/launch.py
5 | # Copyright (c) Facebook, Inc. and its affiliates.
6 | # Copyright (c) Megvii, Inc. and its affiliates.
7 |
8 | import sys
9 | from datetime import timedelta
10 | from loguru import logger
11 |
12 | import torch
13 | import torch.distributed as dist
14 | import torch.multiprocessing as mp
15 |
16 | import cfp.utils.dist as comm
17 |
18 | __all__ = ["launch"]
19 |
20 |
21 | DEFAULT_TIMEOUT = timedelta(minutes=30)
22 |
23 |
24 | def _find_free_port():
25 | """
26 | Find an available port of current machine / node.
27 | """
28 | import socket
29 |
30 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
31 | # Binding to port 0 will cause the OS to find an available port for us
32 | sock.bind(("", 0))
33 | port = sock.getsockname()[1]
34 | sock.close()
35 | # NOTE: there is still a chance the port could be taken by other processes.
36 | return port
37 |
38 |
39 | def launch(
40 | main_func,
41 | num_gpus_per_machine,
42 | num_machines=1,
43 | machine_rank=0,
44 | backend="nccl",
45 | dist_url=None,
46 | args=(),
47 | timeout=DEFAULT_TIMEOUT,
48 | ):
49 | """
50 | Args:
51 | main_func: a function that will be called by `main_func(*args)`
52 | num_machines (int): the total number of machines
53 | machine_rank (int): the rank of this machine (one per machine)
54 | dist_url (str): url to connect to for distributed training, including protocol
55 | e.g. "tcp://127.0.0.1:8686".
56 | Can be set to auto to automatically select a free port on localhost
57 | args (tuple): arguments passed to main_func
58 | """
59 | world_size = num_machines * num_gpus_per_machine
60 | if world_size > 1:
61 | # https://github.com/pytorch/pytorch/pull/14391
62 | # TODO prctl in spawned processes
63 |
64 | if dist_url == "auto":
65 | assert (
66 | num_machines == 1
67 | ), "dist_url=auto cannot work with distributed training."
68 | port = _find_free_port()
69 | dist_url = f"tcp://127.0.0.1:{port}"
70 |
71 | start_method = "spawn"
72 | cache = vars(args[1]).get("cache", False)
73 |
74 | # To use numpy memmap for caching image into RAM, we have to use fork method
75 | if cache:
76 | assert sys.platform != "win32", (
77 | "As Windows platform doesn't support fork method, "
78 | "do not add --cache in your training command."
79 | )
80 | start_method = "fork"
81 |
82 | mp.start_processes(
83 | _distributed_worker,
84 | nprocs=num_gpus_per_machine,
85 | args=(
86 | main_func,
87 | world_size,
88 | num_gpus_per_machine,
89 | machine_rank,
90 | backend,
91 | dist_url,
92 | args,
93 | ),
94 | daemon=False,
95 | start_method=start_method,
96 | )
97 | else:
98 | main_func(*args)
99 |
100 |
101 | def _distributed_worker(
102 | local_rank,
103 | main_func,
104 | world_size,
105 | num_gpus_per_machine,
106 | machine_rank,
107 | backend,
108 | dist_url,
109 | args,
110 | timeout=DEFAULT_TIMEOUT,
111 | ):
112 | assert (
113 | torch.cuda.is_available()
114 | ), "cuda is not available. Please check your installation."
115 | global_rank = machine_rank * num_gpus_per_machine + local_rank
116 | logger.info("Rank {} initialization finished.".format(global_rank))
117 | try:
118 | dist.init_process_group(
119 | backend=backend,
120 | init_method=dist_url,
121 | world_size=world_size,
122 | rank=global_rank,
123 | timeout=timeout,
124 | )
125 | except Exception:
126 | logger.error("Process group URL: {}".format(dist_url))
127 | raise
128 |
129 | # Setup the local process group (which contains ranks within the same machine)
130 | assert comm._LOCAL_PROCESS_GROUP is None
131 | num_machines = world_size // num_gpus_per_machine
132 | for i in range(num_machines):
133 | ranks_on_i = list(
134 | range(i * num_gpus_per_machine, (i + 1) * num_gpus_per_machine)
135 | )
136 | pg = dist.new_group(ranks_on_i)
137 | if i == machine_rank:
138 | comm._LOCAL_PROCESS_GROUP = pg
139 |
140 | # synchronize is needed here to prevent a possible timeout after calling init_process_group
141 | # See: https://github.com/facebookresearch/maskrcnn-benchmark/issues/172
142 | comm.synchronize()
143 |
144 | assert num_gpus_per_machine <= torch.cuda.device_count()
145 | torch.cuda.set_device(local_rank)
146 |
147 | main_func(*args)
148 |
--------------------------------------------------------------------------------
/cfp/utils/boxes.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import numpy as np
6 |
7 | import torch
8 | import torchvision
9 |
10 | __all__ = [
11 | "filter_box",
12 | "postprocess",
13 | "bboxes_iou",
14 | "matrix_iou",
15 | "adjust_box_anns",
16 | "xyxy2xywh",
17 | "xyxy2cxcywh",
18 | ]
19 |
20 |
21 | def filter_box(output, scale_range):
22 | """
23 | output: (N, 5+class) shape
24 | """
25 | min_scale, max_scale = scale_range
26 | w = output[:, 2] - output[:, 0]
27 | h = output[:, 3] - output[:, 1]
28 | keep = (w * h > min_scale * min_scale) & (w * h < max_scale * max_scale)
29 | return output[keep]
30 |
31 |
32 | def postprocess(prediction, num_classes, conf_thre=0.7, nms_thre=0.45, class_agnostic=False):
33 | box_corner = prediction.new(prediction.shape)
34 | box_corner[:, :, 0] = prediction[:, :, 0] - prediction[:, :, 2] / 2
35 | box_corner[:, :, 1] = prediction[:, :, 1] - prediction[:, :, 3] / 2
36 | box_corner[:, :, 2] = prediction[:, :, 0] + prediction[:, :, 2] / 2
37 | box_corner[:, :, 3] = prediction[:, :, 1] + prediction[:, :, 3] / 2
38 | prediction[:, :, :4] = box_corner[:, :, :4]
39 |
40 | output = [None for _ in range(len(prediction))]
41 | for i, image_pred in enumerate(prediction):
42 |
43 | # If none are remaining => process next image
44 | if not image_pred.size(0):
45 | continue
46 | # Get score and class with highest confidence
47 | class_conf, class_pred = torch.max(image_pred[:, 5: 5 + num_classes], 1, keepdim=True)
48 |
49 | conf_mask = (image_pred[:, 4] * class_conf.squeeze() >= conf_thre).squeeze()
50 | # Detections ordered as (x1, y1, x2, y2, obj_conf, class_conf, class_pred)
51 | detections = torch.cat((image_pred[:, :5], class_conf, class_pred.float()), 1)
52 | detections = detections[conf_mask]
53 | if not detections.size(0):
54 | continue
55 |
56 | if class_agnostic:
57 | nms_out_index = torchvision.ops.nms(
58 | detections[:, :4],
59 | detections[:, 4] * detections[:, 5],
60 | nms_thre,
61 | )
62 | else:
63 | nms_out_index = torchvision.ops.batched_nms(
64 | detections[:, :4],
65 | detections[:, 4] * detections[:, 5],
66 | detections[:, 6],
67 | nms_thre,
68 | )
69 |
70 | detections = detections[nms_out_index]
71 | if output[i] is None:
72 | output[i] = detections
73 | else:
74 | output[i] = torch.cat((output[i], detections))
75 |
76 | return output
77 |
78 |
79 | def bboxes_iou(bboxes_a, bboxes_b, xyxy=True):
80 | if bboxes_a.shape[1] != 4 or bboxes_b.shape[1] != 4:
81 | raise IndexError
82 |
83 | if xyxy:
84 | tl = torch.max(bboxes_a[:, None, :2], bboxes_b[:, :2])
85 | br = torch.min(bboxes_a[:, None, 2:], bboxes_b[:, 2:])
86 | area_a = torch.prod(bboxes_a[:, 2:] - bboxes_a[:, :2], 1)
87 | area_b = torch.prod(bboxes_b[:, 2:] - bboxes_b[:, :2], 1)
88 | else:
89 | tl = torch.max(
90 | (bboxes_a[:, None, :2] - bboxes_a[:, None, 2:] / 2),
91 | (bboxes_b[:, :2] - bboxes_b[:, 2:] / 2),
92 | )
93 | br = torch.min(
94 | (bboxes_a[:, None, :2] + bboxes_a[:, None, 2:] / 2),
95 | (bboxes_b[:, :2] + bboxes_b[:, 2:] / 2),
96 | )
97 |
98 | area_a = torch.prod(bboxes_a[:, 2:], 1)
99 | area_b = torch.prod(bboxes_b[:, 2:], 1)
100 | en = (tl < br).type(tl.type()).prod(dim=2)
101 | area_i = torch.prod(br - tl, 2) * en # * ((tl < br).all())
102 | return area_i / (area_a[:, None] + area_b - area_i)
103 |
104 |
105 | def matrix_iou(a, b):
106 | """
107 | return iou of a and b, numpy version for data augenmentation
108 | """
109 | lt = np.maximum(a[:, np.newaxis, :2], b[:, :2])
110 | rb = np.minimum(a[:, np.newaxis, 2:], b[:, 2:])
111 |
112 | area_i = np.prod(rb - lt, axis=2) * (lt < rb).all(axis=2)
113 | area_a = np.prod(a[:, 2:] - a[:, :2], axis=1)
114 | area_b = np.prod(b[:, 2:] - b[:, :2], axis=1)
115 | return area_i / (area_a[:, np.newaxis] + area_b - area_i + 1e-12)
116 |
117 |
118 | def adjust_box_anns(bbox, scale_ratio, padw, padh, w_max, h_max):
119 | bbox[:, 0::2] = np.clip(bbox[:, 0::2] * scale_ratio + padw, 0, w_max)
120 | bbox[:, 1::2] = np.clip(bbox[:, 1::2] * scale_ratio + padh, 0, h_max)
121 | return bbox
122 |
123 |
124 | def xyxy2xywh(bboxes):
125 | bboxes[:, 2] = bboxes[:, 2] - bboxes[:, 0]
126 | bboxes[:, 3] = bboxes[:, 3] - bboxes[:, 1]
127 | return bboxes
128 |
129 |
130 | def xyxy2cxcywh(bboxes):
131 | bboxes[:, 2] = bboxes[:, 2] - bboxes[:, 0]
132 | bboxes[:, 3] = bboxes[:, 3] - bboxes[:, 1]
133 | bboxes[:, 0] = bboxes[:, 0] + bboxes[:, 2] * 0.5
134 | bboxes[:, 1] = bboxes[:, 1] + bboxes[:, 3] * 0.5
135 | return bboxes
136 |
--------------------------------------------------------------------------------
/exps/example/yolox_voc/yolox_voc_s.py:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 | import os
3 |
4 | import torch
5 | import torch.distributed as dist
6 |
7 | from cfp.data import get_yolox_datadir
8 | from cfp.exp import Exp as MyExp
9 |
10 |
11 | class Exp(MyExp):
12 | def __init__(self):
13 | super(Exp, self).__init__()
14 | self.num_classes = 20
15 | self.depth = 0.33
16 | self.width = 0.50
17 | self.warmup_epochs = 1
18 |
19 | # ---------- transform config ------------ #
20 | self.mosaic_prob = 1.0
21 | self.mixup_prob = 1.0
22 | self.hsv_prob = 1.0
23 | self.flip_prob = 0.5
24 |
25 | self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]
26 |
27 | def get_data_loader(self, batch_size, is_distributed, no_aug=False, cache_img=False):
28 | from yolox.data import (
29 | VOCDetection,
30 | TrainTransform,
31 | YoloBatchSampler,
32 | DataLoader,
33 | InfiniteSampler,
34 | MosaicDetection,
35 | worker_init_reset_seed,
36 | )
37 | from yolox.utils import (
38 | wait_for_the_master,
39 | get_local_rank,
40 | )
41 | local_rank = get_local_rank()
42 |
43 | with wait_for_the_master(local_rank):
44 | dataset = VOCDetection(
45 | data_dir=os.path.join(get_yolox_datadir(), "/home/10102006/dataset/voc/VOCdevkit/"), # VOCdevkit
46 | image_sets=[('VOC2007', 'trainval'), ('VOC2012', 'trainval')], # ('2007', 'trainval'), ('2012', 'trainval')
47 | img_size=self.input_size,
48 | preproc=TrainTransform(
49 | max_labels=50,
50 | flip_prob=self.flip_prob,
51 | hsv_prob=self.hsv_prob),
52 | cache=cache_img,
53 | )
54 |
55 | dataset = MosaicDetection(
56 | dataset,
57 | mosaic=not no_aug,
58 | img_size=self.input_size,
59 | preproc=TrainTransform(
60 | max_labels=120,
61 | flip_prob=self.flip_prob,
62 | hsv_prob=self.hsv_prob),
63 | degrees=self.degrees,
64 | translate=self.translate,
65 | mosaic_scale=self.mosaic_scale,
66 | mixup_scale=self.mixup_scale,
67 | shear=self.shear,
68 | enable_mixup=self.enable_mixup,
69 | mosaic_prob=self.mosaic_prob,
70 | mixup_prob=self.mixup_prob,
71 | )
72 |
73 | self.dataset = dataset
74 |
75 | if is_distributed:
76 | batch_size = batch_size // dist.get_world_size()
77 |
78 | sampler = InfiniteSampler(
79 | len(self.dataset), seed=self.seed if self.seed else 0
80 | )
81 |
82 | batch_sampler = YoloBatchSampler(
83 | sampler=sampler,
84 | batch_size=batch_size,
85 | drop_last=False,
86 | mosaic=not no_aug,
87 | )
88 |
89 | dataloader_kwargs = {"num_workers": self.data_num_workers, "pin_memory": True}
90 | dataloader_kwargs["batch_sampler"] = batch_sampler
91 |
92 | # Make sure each process has different random seed, especially for 'fork' method
93 | dataloader_kwargs["worker_init_fn"] = worker_init_reset_seed
94 |
95 | train_loader = DataLoader(self.dataset, **dataloader_kwargs)
96 |
97 | return train_loader
98 |
99 | def get_eval_loader(self, batch_size, is_distributed, testdev=False, legacy=False):
100 | from yolox.data import VOCDetection, ValTransform
101 |
102 | valdataset = VOCDetection(
103 | data_dir=os.path.join(get_yolox_datadir(), "VOCdevkit"),
104 | image_sets=[('2007', 'test')],
105 | img_size=self.test_size,
106 | preproc=ValTransform(legacy=legacy),
107 | )
108 |
109 | if is_distributed:
110 | batch_size = batch_size // dist.get_world_size()
111 | sampler = torch.utils.data.distributed.DistributedSampler(
112 | valdataset, shuffle=False
113 | )
114 | else:
115 | sampler = torch.utils.data.SequentialSampler(valdataset)
116 |
117 | dataloader_kwargs = {
118 | "num_workers": self.data_num_workers,
119 | "pin_memory": True,
120 | "sampler": sampler,
121 | }
122 | dataloader_kwargs["batch_size"] = batch_size
123 | val_loader = torch.utils.data.DataLoader(valdataset, **dataloader_kwargs)
124 |
125 | return val_loader
126 |
127 | def get_evaluator(self, batch_size, is_distributed, testdev=False, legacy=False):
128 | from yolox.evaluators import VOCEvaluator
129 |
130 | val_loader = self.get_eval_loader(batch_size, is_distributed, testdev, legacy)
131 | evaluator = VOCEvaluator(
132 | dataloader=val_loader,
133 | img_size=self.test_size,
134 | confthre=self.test_conf,
135 | nmsthre=self.nmsthre,
136 | num_classes=self.num_classes,
137 | )
138 | return evaluator
139 |
--------------------------------------------------------------------------------
/demo/ncnn/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/demo/MegEngine/python/models/darknet.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- encoding: utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import megengine.module as M
6 |
7 | from .network_blocks import BaseConv, CSPLayer, DWConv, Focus, ResLayer, SPPBottleneck
8 |
9 |
10 | class Darknet(M.Module):
11 | # number of blocks from dark2 to dark5.
12 | depth2blocks = {21: [1, 2, 2, 1], 53: [2, 8, 8, 4]}
13 |
14 | def __init__(
15 | self, depth, in_channels=3, stem_out_channels=32, out_features=("dark3", "dark4", "dark5"),
16 | ):
17 | """
18 | Args:
19 | depth (int): depth of darknet used in model, usually use [21, 53] for this param.
20 | in_channels (int): number of input channels, for example, use 3 for RGB image.
21 | stem_out_channels (int): number of output chanels of darknet stem.
22 | It decides channels of darknet layer2 to layer5.
23 | out_features (Tuple[str]): desired output layer name.
24 | """
25 | super().__init__()
26 | assert out_features, "please provide output features of Darknet"
27 | self.out_features = out_features
28 | self.stem = M.Sequential(
29 | BaseConv(in_channels, stem_out_channels, ksize=3, stride=1, act="lrelu"),
30 | *self.make_group_layer(stem_out_channels, num_blocks=1, stride=2),
31 | )
32 | in_channels = stem_out_channels * 2 # 64
33 |
34 | num_blocks = Darknet.depth2blocks[depth]
35 | # create darknet with `stem_out_channels` and `num_blocks` layers.
36 | # to make model structure more clear, we don't use `for` statement in python.
37 | self.dark2 = M.Sequential(*self.make_group_layer(in_channels, num_blocks[0], stride=2))
38 | in_channels *= 2 # 128
39 | self.dark3 = M.Sequential(*self.make_group_layer(in_channels, num_blocks[1], stride=2))
40 | in_channels *= 2 # 256
41 | self.dark4 = M.Sequential(*self.make_group_layer(in_channels, num_blocks[2], stride=2))
42 | in_channels *= 2 # 512
43 |
44 | self.dark5 = M.Sequential(
45 | *self.make_group_layer(in_channels, num_blocks[3], stride=2),
46 | *self.make_spp_block([in_channels, in_channels * 2], in_channels * 2),
47 | )
48 |
49 | def make_group_layer(self, in_channels: int, num_blocks: int, stride: int = 1):
50 | "starts with conv layer then has `num_blocks` `ResLayer`"
51 | return [
52 | BaseConv(in_channels, in_channels * 2, ksize=3, stride=stride, act="lrelu"),
53 | *[(ResLayer(in_channels * 2)) for _ in range(num_blocks)]
54 | ]
55 |
56 | def make_spp_block(self, filters_list, in_filters):
57 | m = M.Sequential(
58 | *[
59 | BaseConv(in_filters, filters_list[0], 1, stride=1, act="lrelu"),
60 | BaseConv(filters_list[0], filters_list[1], 3, stride=1, act="lrelu"),
61 | SPPBottleneck(
62 | in_channels=filters_list[1],
63 | out_channels=filters_list[0],
64 | activation="lrelu"
65 | ),
66 | BaseConv(filters_list[0], filters_list[1], 3, stride=1, act="lrelu"),
67 | BaseConv(filters_list[1], filters_list[0], 1, stride=1, act="lrelu"),
68 | ]
69 | )
70 | return m
71 |
72 | def forward(self, x):
73 | outputs = {}
74 | x = self.stem(x)
75 | outputs["stem"] = x
76 | x = self.dark2(x)
77 | outputs["dark2"] = x
78 | x = self.dark3(x)
79 | outputs["dark3"] = x
80 | x = self.dark4(x)
81 | outputs["dark4"] = x
82 | x = self.dark5(x)
83 | outputs["dark5"] = x
84 | return {k: v for k, v in outputs.items() if k in self.out_features}
85 |
86 |
87 | class CSPDarknet(M.Module):
88 |
89 | def __init__(
90 | self, dep_mul, wid_mul,
91 | out_features=("dark3", "dark4", "dark5"),
92 | depthwise=False, act="silu",
93 | ):
94 | super().__init__()
95 | assert out_features, "please provide output features of Darknet"
96 | self.out_features = out_features
97 | Conv = DWConv if depthwise else BaseConv
98 |
99 | base_channels = int(wid_mul * 64) # 64
100 | base_depth = max(round(dep_mul * 3), 1) # 3
101 |
102 | # stem
103 | self.stem = Focus(3, base_channels, ksize=3, act=act)
104 |
105 | # dark2
106 | self.dark2 = M.Sequential(
107 | Conv(base_channels, base_channels * 2, 3, 2, act=act),
108 | CSPLayer(
109 | base_channels * 2, base_channels * 2,
110 | n=base_depth, depthwise=depthwise, act=act
111 | ),
112 | )
113 |
114 | # dark3
115 | self.dark3 = M.Sequential(
116 | Conv(base_channels * 2, base_channels * 4, 3, 2, act=act),
117 | CSPLayer(
118 | base_channels * 4, base_channels * 4,
119 | n=base_depth * 3, depthwise=depthwise, act=act,
120 | ),
121 | )
122 |
123 | # dark4
124 | self.dark4 = M.Sequential(
125 | Conv(base_channels * 4, base_channels * 8, 3, 2, act=act),
126 | CSPLayer(
127 | base_channels * 8, base_channels * 8,
128 | n=base_depth * 3, depthwise=depthwise, act=act,
129 | ),
130 | )
131 |
132 | # dark5
133 | self.dark5 = M.Sequential(
134 | Conv(base_channels * 8, base_channels * 16, 3, 2, act=act),
135 | SPPBottleneck(base_channels * 16, base_channels * 16, activation=act),
136 | CSPLayer(
137 | base_channels * 16, base_channels * 16, n=base_depth,
138 | shortcut=False, depthwise=depthwise, act=act,
139 | ),
140 | )
141 |
142 | def forward(self, x):
143 | outputs = {}
144 | x = self.stem(x)
145 | outputs["stem"] = x
146 | x = self.dark2(x)
147 | outputs["dark2"] = x
148 | x = self.dark3(x)
149 | outputs["dark3"] = x
150 | x = self.dark4(x)
151 | outputs["dark4"] = x
152 | x = self.dark5(x)
153 | outputs["dark5"] = x
154 | return {k: v for k, v in outputs.items() if k in self.out_features}
155 |
--------------------------------------------------------------------------------
/demo/OpenVINO/python/openvino_inference.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | # Copyright (C) 2018-2021 Intel Corporation
4 | # SPDX-License-Identifier: Apache-2.0
5 | # Copyright (c) Megvii, Inc. and its affiliates.
6 |
7 | import argparse
8 | import logging as log
9 | import os
10 | import sys
11 |
12 | import cv2
13 | import numpy as np
14 |
15 | from openvino.inference_engine import IECore
16 |
17 | from cfp.data.data_augment import preproc as preprocess
18 | from cfp.data.datasets import COCO_CLASSES
19 | from cfp.utils import mkdir, multiclass_nms, demo_postprocess, vis
20 |
21 |
22 | def parse_args() -> argparse.Namespace:
23 | """Parse and return command line arguments"""
24 | parser = argparse.ArgumentParser(add_help=False)
25 | args = parser.add_argument_group('Options')
26 | args.add_argument(
27 | '-h',
28 | '--help',
29 | action='help',
30 | help='Show this help message and exit.')
31 | args.add_argument(
32 | '-m',
33 | '--model',
34 | required=True,
35 | type=str,
36 | help='Required. Path to an .xml or .onnx file with a trained model.')
37 | args.add_argument(
38 | '-i',
39 | '--input',
40 | required=True,
41 | type=str,
42 | help='Required. Path to an image file.')
43 | args.add_argument(
44 | '-o',
45 | '--output_dir',
46 | type=str,
47 | default='demo_output',
48 | help='Path to your output dir.')
49 | args.add_argument(
50 | '-s',
51 | '--score_thr',
52 | type=float,
53 | default=0.3,
54 | help="Score threshould to visualize the result.")
55 | args.add_argument(
56 | '-d',
57 | '--device',
58 | default='CPU',
59 | type=str,
60 | help='Optional. Specify the target device to infer on; CPU, GPU, \
61 | MYRIAD, HDDL or HETERO: is acceptable. The sample will look \
62 | for a suitable plugin for device specified. Default value \
63 | is CPU.')
64 | args.add_argument(
65 | '--labels',
66 | default=None,
67 | type=str,
68 | help='Option:al. Path to a labels mapping file.')
69 | args.add_argument(
70 | '-nt',
71 | '--number_top',
72 | default=10,
73 | type=int,
74 | help='Optional. Number of top results.')
75 | return parser.parse_args()
76 |
77 |
78 | def main():
79 | log.basicConfig(format='[ %(levelname)s ] %(message)s', level=log.INFO, stream=sys.stdout)
80 | args = parse_args()
81 |
82 | # ---------------------------Step 1. Initialize inference engine core--------------------------------------------------
83 | log.info('Creating Inference Engine')
84 | ie = IECore()
85 |
86 | # ---------------------------Step 2. Read a model in OpenVINO Intermediate Representation or ONNX format---------------
87 | log.info(f'Reading the network: {args.model}')
88 | # (.xml and .bin files) or (.onnx file)
89 | net = ie.read_network(model=args.model)
90 |
91 | if len(net.input_info) != 1:
92 | log.error('Sample supports only single input topologies')
93 | return -1
94 | if len(net.outputs) != 1:
95 | log.error('Sample supports only single output topologies')
96 | return -1
97 |
98 | # ---------------------------Step 3. Configure input & output----------------------------------------------------------
99 | log.info('Configuring input and output blobs')
100 | # Get names of input and output blobs
101 | input_blob = next(iter(net.input_info))
102 | out_blob = next(iter(net.outputs))
103 |
104 | # Set input and output precision manually
105 | net.input_info[input_blob].precision = 'FP32'
106 | net.outputs[out_blob].precision = 'FP16'
107 |
108 | # Get a number of classes recognized by a model
109 | num_of_classes = max(net.outputs[out_blob].shape)
110 |
111 | # ---------------------------Step 4. Loading model to the device-------------------------------------------------------
112 | log.info('Loading the model to the plugin')
113 | exec_net = ie.load_network(network=net, device_name=args.device)
114 |
115 | # ---------------------------Step 5. Create infer request--------------------------------------------------------------
116 | # load_network() method of the IECore class with a specified number of requests (default 1) returns an ExecutableNetwork
117 | # instance which stores infer requests. So you already created Infer requests in the previous step.
118 |
119 | # ---------------------------Step 6. Prepare input---------------------------------------------------------------------
120 | origin_img = cv2.imread(args.input)
121 | _, _, h, w = net.input_info[input_blob].input_data.shape
122 | image, ratio = preprocess(origin_img, (h, w))
123 |
124 | # ---------------------------Step 7. Do inference----------------------------------------------------------------------
125 | log.info('Starting inference in synchronous mode')
126 | res = exec_net.infer(inputs={input_blob: image})
127 |
128 | # ---------------------------Step 8. Process output--------------------------------------------------------------------
129 | res = res[out_blob]
130 |
131 | predictions = demo_postprocess(res, (h, w), p6=False)[0]
132 |
133 | boxes = predictions[:, :4]
134 | scores = predictions[:, 4, None] * predictions[:, 5:]
135 |
136 | boxes_xyxy = np.ones_like(boxes)
137 | boxes_xyxy[:, 0] = boxes[:, 0] - boxes[:, 2]/2.
138 | boxes_xyxy[:, 1] = boxes[:, 1] - boxes[:, 3]/2.
139 | boxes_xyxy[:, 2] = boxes[:, 0] + boxes[:, 2]/2.
140 | boxes_xyxy[:, 3] = boxes[:, 1] + boxes[:, 3]/2.
141 | boxes_xyxy /= ratio
142 | dets = multiclass_nms(boxes_xyxy, scores, nms_thr=0.45, score_thr=0.1)
143 |
144 | if dets is not None:
145 | final_boxes = dets[:, :4]
146 | final_scores, final_cls_inds = dets[:, 4], dets[:, 5]
147 | origin_img = vis(origin_img, final_boxes, final_scores, final_cls_inds,
148 | conf=args.score_thr, class_names=COCO_CLASSES)
149 |
150 | mkdir(args.output_dir)
151 | output_path = os.path.join(args.output_dir, os.path.basename(args.input))
152 | cv2.imwrite(output_path, origin_img)
153 |
154 |
155 | if __name__ == '__main__':
156 | sys.exit(main())
157 |
--------------------------------------------------------------------------------