Compare commits

...

14 Commits

Author SHA1 Message Date
Cathy0908
901ff18693
fix error when detection output is null (#351) 2025-05-09 18:24:37 +08:00
lostkevin
31897984d8
implement onnx export for inception3/4, resnext, mobilenetv2 (#346)
* add inceptionv4 backbone/training settings
* add converted backbone, top-1 acc 80.08
2024-07-18 16:52:56 +08:00
gulou
8c90ceaf84
add resnet export of onnx (#341)
* add checkpoint_sync_export for resnet config
2024-07-02 19:39:28 +08:00
gulou
1d1ac8aa5e
fix pose prediction of result error (#337)
update pose predictor
2024-04-09 16:27:23 +08:00
zouxinyi0625
5ba3057ff8
Modify yolox config setting(#339) 2024-03-01 16:20:12 +08:00
TTL
09232dac33
fix style: code style ``python`` not work well (#338)
Simple verification

```python
from easycv.apis import *
```

You can also verify your installation using following quick-start examples
2024-02-29 16:27:14 +08:00
Catl Neo
af4e9d5d19
Fix typo (#336)
* Update README.md and  cityscapes.py
2024-02-01 14:06:09 +08:00
Cathy0908
cecb36a9ce
fix export pose model (#335) 2024-01-11 10:22:01 +08:00
Cathy0908
447a1665c6
update version (#334) 2023-11-15 17:26:00 +08:00
Cathy0908
0b3045ff87
fix from collections import Sequence for py3.10 (#333) 2023-11-15 17:21:49 +08:00
Cathy0908
d2b95eac13
update version to v0.11.5 (#332) 2023-11-01 15:32:11 +08:00
Cathy0908
fc16bc1b10
fix timm version compatibility (#331)
* Fix timm version compatibility
2023-11-01 14:49:41 +08:00
gulou
8c3ba59aaf
Features/self test onnx (#330)
add yolox onnx export method
2023-10-31 14:56:08 +08:00
Cathy0908
db33ced143
fix numpy version compatibility (#327)
* fix numpy version compatibility
2023-08-18 14:05:15 +08:00
43 changed files with 1125 additions and 169 deletions

View File

@ -248,7 +248,7 @@ Please refer to the following model zoo for more details.
## Data Hub
EasyCV have collected dataset info for different senarios, making it easy for users to finetune or evaluate models in EasyCV model zoo.
EasyCV have collected dataset info for different scenarios, making it easy for users to finetune or evaluate models in EasyCV model zoo.
Please refer to [data_hub.md](docs/source/data_hub.md).

View File

@ -4,16 +4,31 @@ num_classes = 1000
# model settings
model = dict(
type='Classification',
backbone=dict(type='Inception3'),
head=dict(
backbone=dict(type='Inception3', num_classes=1000),
head=[
dict(
type='ClsHead',
with_avg_pool=True,
with_fc=False,
in_channels=2048,
loss_config=dict(
type='CrossEntropyLossWithLabelSmooth',
label_smooth=0,
),
num_classes=num_classes))
num_classes=num_classes,
input_feature_index=[1],
),
dict(
type='ClsHead',
with_fc=False,
in_channels=768,
loss_config=dict(
type='CrossEntropyLossWithLabelSmooth',
label_smooth=0,
),
num_classes=num_classes,
input_feature_index=[0],
)
])
class_list = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13',
@ -196,3 +211,5 @@ log_config = dict(
interval=10,
hooks=[dict(type='TextLoggerHook'),
dict(type='TensorboardLoggerHook')])
export = dict(export_type='raw', export_neck=True)

View File

@ -0,0 +1,33 @@
_base_ = 'configs/classification/imagenet/inception/inceptionv3_b32x8_100e.py'
num_classes = 1000
# model settings
model = dict(
type='Classification',
backbone=dict(type='Inception4', num_classes=num_classes),
head=[
dict(
type='ClsHead',
with_fc=False,
in_channels=1536,
loss_config=dict(
type='CrossEntropyLossWithLabelSmooth',
label_smooth=0,
),
num_classes=num_classes,
input_feature_index=[1],
),
dict(
type='ClsHead',
with_fc=False,
in_channels=768,
loss_config=dict(
type='CrossEntropyLossWithLabelSmooth',
label_smooth=0,
),
num_classes=num_classes,
input_feature_index=[0],
)
])
img_norm_cfg = dict(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])

View File

@ -0,0 +1,46 @@
# A config with the optimization settings from https://arxiv.org/pdf/1602.07261
# May run with 20 GPUs
_base_ = 'configs/classification/imagenet/inception/inceptionv3_b32x8_100e.py'
num_classes = 1000
# model settings
model = dict(
type='Classification',
backbone=dict(type='Inception4', num_classes=num_classes),
head=[
dict(
type='ClsHead',
with_fc=False,
in_channels=1536,
loss_config=dict(
type='CrossEntropyLossWithLabelSmooth',
label_smooth=0,
),
num_classes=num_classes,
input_feature_index=[1],
),
dict(
type='ClsHead',
with_fc=False,
in_channels=768,
loss_config=dict(
type='CrossEntropyLossWithLabelSmooth',
label_smooth=0,
),
num_classes=num_classes,
input_feature_index=[0],
)
])
img_norm_cfg = dict(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
# optimizer
optimizer = dict(
type='RMSprop', lr=0.045, momentum=0.9, weight_decay=0.9, eps=1.0)
# learning policy
lr_config = dict(policy='exp', gamma=0.96954) # gamma**2 ~ 0.94
checkpoint_config = dict(interval=10)
# runtime settings
total_epochs = 200

View File

@ -13,7 +13,8 @@ model = dict(
type='CrossEntropyLossWithLabelSmooth',
label_smooth=0,
),
num_classes=num_classes))
num_classes=num_classes),
pretrained=True)
# optimizer
optimizer = dict(type='SGD', lr=0.1, momentum=0.9, weight_decay=0.0001)
@ -25,4 +26,4 @@ checkpoint_config = dict(interval=5)
# runtime settings
total_epochs = 100
checkpoint_sync_export = True
export = dict(export_neck=True)
export = dict(export_type='raw', export_neck=True)

View File

@ -6,3 +6,6 @@ model = dict(
depth=50,
out_indices=[4], # 0: conv-1, x: stage-x
norm_cfg=dict(type='BN')))
checkpoint_sync_export = True
export = dict(export_type='raw', export_neck=True)

View File

@ -19,7 +19,8 @@ model = dict(
type='CrossEntropyLossWithLabelSmooth',
label_smooth=0,
),
num_classes=num_classes))
num_classes=num_classes),
pretrained=True)
# optimizer
optimizer = dict(type='SGD', lr=0.1, momentum=0.9, weight_decay=0.0001)
@ -30,3 +31,4 @@ checkpoint_config = dict(interval=10)
# runtime settings
total_epochs = 100
export = dict(export_type='raw', export_neck=True)

View File

@ -49,14 +49,14 @@ img_norm_cfg = dict(
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
train_pipeline = [
dict(type='MMMosaic', img_scale='${img_scale}', pad_val=114.0),
dict(type='MMMosaic', img_scale=tuple(img_scale), pad_val=114.0),
dict(
type='MMRandomAffine',
scaling_ratio_range='${scale_ratio}',
border=['-${img_scale}[0] // 2', '-${img_scale}[1] // 2']),
scaling_ratio_range=scale_ratio,
border=[img_scale[0] // 2, img_scale[1] // 2]),
dict(
type='MMMixUp', # s m x l; tiny nano will detele
img_scale='${img_scale}',
img_scale=tuple(img_scale),
ratio_range=(0.8, 1.6),
pad_val=114.0),
dict(
@ -70,45 +70,43 @@ train_pipeline = [
dict(type='MMPad', pad_to_square=True, pad_val=(114.0, 114.0, 114.0)),
dict(
type='MMNormalize',
mean='${img_norm_cfg.mean}',
std='${img_norm_cfg.std}',
to_rgb='${img_norm_cfg.to_rgb}'),
mean=img_norm_cfg['mean'],
std=img_norm_cfg['std'],
to_rgb=img_norm_cfg['to_rgb']),
dict(type='DefaultFormatBundle'),
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])
]
test_pipeline = [
dict(type='MMResize', img_scale='${img_scale}', keep_ratio=True),
dict(type='MMResize', img_scale=img_scale, keep_ratio=True),
dict(type='MMPad', pad_to_square=True, pad_val=(114.0, 114.0, 114.0)),
dict(
type='MMNormalize',
mean='${img_norm_cfg.mean}',
std='${img_norm_cfg.std}',
to_rgb='${img_norm_cfg.to_rgb}'),
mean=img_norm_cfg['mean'],
std=img_norm_cfg['std'],
to_rgb=img_norm_cfg['to_rgb']),
dict(type='DefaultFormatBundle'),
dict(type='Collect', keys=['img'])
]
data = dict(
imgs_per_gpu=16,
workers_per_gpu=4,
train=dict(
train_path = 'data/coco/train2017.manifest'
val_path = 'data/coco/val2017.manifest'
train_dataset = dict(
type='DetImagesMixDataset',
data_source=dict(
type='DetSourcePAI',
path='data/coco/train2017.manifest',
classes='${CLASSES}'),
pipeline='${train_pipeline}',
dynamic_scale='${img_scale}'),
val=dict(
data_source=dict(type='DetSourcePAI', path=train_path, classes=CLASSES),
pipeline=train_pipeline,
dynamic_scale=tuple(img_scale))
val_dataset = dict(
type='DetImagesMixDataset',
imgs_per_gpu=2,
data_source=dict(
type='DetSourcePAI',
path='data/coco/val2017.manifest',
classes='${CLASSES}'),
pipeline='${test_pipeline}',
data_source=dict(type='DetSourcePAI', path=val_path, classes=CLASSES),
pipeline=test_pipeline,
dynamic_scale=None,
label_padding=False))
label_padding=False)
data = dict(
imgs_per_gpu=16, workers_per_gpu=4, train=train_dataset, val=val_dataset)
# additional hooks
interval = 10
@ -120,14 +118,14 @@ custom_hooks = [
priority=48),
dict(
type='SyncRandomSizeHook',
ratio_range='${random_size}',
img_scale='${img_scale}',
interval='${interval}',
ratio_range=random_size,
img_scale=img_scale,
interval=interval,
priority=48),
dict(
type='SyncNormHook',
num_last_epochs=15,
interval='${interval}',
interval=interval,
priority=48)
]
@ -135,23 +133,23 @@ custom_hooks = [
vis_num = 20
score_thr = 0.5
eval_config = dict(
interval='${interval}',
interval=interval,
gpu_collect=False,
visualization_config=dict(
vis_num='${vis_num}',
score_thr='${score_thr}',
vis_num=vis_num,
score_thr=score_thr,
) # show by TensorboardLoggerHookV2
)
eval_pipelines = [
dict(
mode='test',
data='${data.val}',
data=val_dataset,
evaluators=[dict(type='CocoDetectionEvaluator', classes=CLASSES)],
)
]
checkpoint_config = dict(interval='${interval}')
checkpoint_config = dict(interval=interval)
# optimizer
# basic_lr_per_img = 0.01 / 64.0
optimizer = dict(

View File

@ -157,6 +157,52 @@ def _export_jit_and_blade(model, cfg, filename, dummy_inputs, fp16=False):
torch.jit.save(blade_model, ofile)
def _export_onnx_cls(model, model_config, cfg, filename, meta):
support_backbones = {
'ResNet': {
'depth': [50]
},
'MobileNetV2': {},
'Inception3': {},
'Inception4': {},
'ResNeXt': {
'depth': [50]
}
}
if model_config['backbone'].get('type', None) not in support_backbones:
tmp = ' '.join(support_backbones.keys())
info_str = f'Only support export onnx model for {tmp} now!'
raise ValueError(info_str)
configs = support_backbones[model_config['backbone'].get('type')]
for k, v in configs.items():
if v[0].__class__(model_config['backbone'].get(k, None)) not in v:
raise ValueError(
f"Unsupport config for {model_config['backbone'].get('type')}")
# save json config for test_pipline and class
with io.open(
filename +
'.config.json' if filename.endswith('onnx') else filename +
'.onnx.config.json', 'w') as ofile:
json.dump(meta, ofile)
device = 'cuda' if torch.cuda.is_available() else 'cpu'
model.eval()
model.to(device)
img_size = int(cfg.image_size2)
x_input = torch.randn((1, 3, img_size, img_size)).to(device)
torch.onnx.export(
model,
(x_input, 'onnx'),
filename if filename.endswith('onnx') else filename + '.onnx',
export_params=True,
opset_version=12,
do_constant_folding=True,
input_names=['input'],
output_names=['output'],
)
def _export_cls(model, cfg, filename):
""" export cls (cls & metric learning)model and preprocess config
@ -170,6 +216,7 @@ def _export_cls(model, cfg, filename):
else:
export_cfg = dict(export_neck=False)
export_type = export_cfg.get('export_type', 'raw')
export_neck = export_cfg.get('export_neck', True)
label_map_path = cfg.get('label_map_path', None)
class_list = None
@ -232,9 +279,14 @@ def _export_cls(model, cfg, filename):
if export_neck and (k.startswith('neck') or k.startswith('head')):
state_dict[k] = v
if export_type == 'raw':
checkpoint = dict(state_dict=state_dict, meta=meta, author='EasyCV')
with io.open(filename, 'wb') as ofile:
torch.save(checkpoint, ofile)
elif export_type == 'onnx':
_export_onnx_cls(model, model_config, cfg, filename, config)
else:
raise ValueError('Only support export onnx/raw model!')
def _export_yolox(model, cfg, filename):
@ -247,10 +299,10 @@ def _export_yolox(model, cfg, filename):
if hasattr(cfg, 'export'):
export_type = getattr(cfg.export, 'export_type', 'raw')
default_export_type_list = ['raw', 'jit', 'blade']
default_export_type_list = ['raw', 'jit', 'blade', 'onnx']
if export_type not in default_export_type_list:
logging.warning(
'YOLOX-PAI only supports the export type as [raw,jit,blade], otherwise we use raw as default'
'YOLOX-PAI only supports the export type as [raw,jit,blade,onnx], otherwise we use raw as default'
)
export_type = 'raw'
@ -276,7 +328,7 @@ def _export_yolox(model, cfg, filename):
len(img_scale) == 2
), 'Export YoloX predictor config contains img_scale must be (int, int) tuple!'
input = 255 * torch.rand((batch_size, 3) + img_scale)
input = 255 * torch.rand((batch_size, 3) + tuple(img_scale))
# assert use_trt_efficientnms only happens when static_opt=True
if static_opt is not True:
@ -355,6 +407,31 @@ def _export_yolox(model, cfg, filename):
json.dump(config, ofile)
if export_type == 'onnx':
with io.open(
filename + '.config.json' if filename.endswith('onnx')
else filename + '.onnx.config.json', 'w') as ofile:
config = dict(
model=cfg.model,
export=cfg.export,
test_pipeline=cfg.test_pipeline,
classes=cfg.CLASSES)
json.dump(config, ofile)
torch.onnx.export(
model,
input.to(device),
filename if filename.endswith('onnx') else filename +
'.onnx',
export_params=True,
opset_version=12,
do_constant_folding=True,
input_names=['input'],
output_names=['output'],
)
if export_type == 'jit':
with io.open(filename + '.jit', 'wb') as ofile:
torch.jit.save(yolox_trace, ofile)
@ -650,6 +727,12 @@ def _export_pose_topdown(model, cfg, filename, fp16=False, dummy_inputs=None):
model.to(device)
if hasattr(cfg, 'export') and getattr(cfg.export, 'type', 'raw') == 'raw':
from mmcv.utils.path import is_filepath
if hasattr(cfg, 'dataset_info') and is_filepath(cfg.dataset_info):
dataset_info_cfg = Config.fromfile(cfg.dataset_info)
cfg.dataset_info = dataset_info_cfg._cfg_dict['dataset_info']
return _export_common(model, cfg, filename)
def _dummy_inputs(cfg):

View File

@ -467,8 +467,8 @@ class COCOeval:
fps = np.logical_and(
np.logical_not(dtm), np.logical_not(dtIg))
tp_sum = np.cumsum(tps, axis=1).astype(dtype=np.float)
fp_sum = np.cumsum(fps, axis=1).astype(dtype=np.float)
tp_sum = np.cumsum(tps, axis=1).astype(dtype=np.float32)
fp_sum = np.cumsum(fps, axis=1).astype(dtype=np.float32)
for t, (tp, fp) in enumerate(zip(tp_sum, fp_sum)):
tp = np.array(tp)
fp = np.array(fp)

View File

@ -252,7 +252,7 @@ class FaceKeypointsDataAugumentation:
skin_factor_list = [0.6, 0.8, 1.0, 1.2, 1.4]
skin_factor = np.random.choice(skin_factor_list)
img_ycrcb_raw[:, :, 0:1] = np.clip(
img_ycrcb_raw[:, :, 0:1].astype(np.float) * skin_factor, 0,
img_ycrcb_raw[:, :, 0:1].astype(np.float32) * skin_factor, 0,
255).astype(np.uint8)
img = cv2.cvtColor(img_ycrcb_raw, cv2.COLOR_YCR_CB2BGR)

View File

@ -110,16 +110,16 @@ class SegSourceCityscapes(SegSourceRaw):
'')[:-len(self.img_suffix[0])]
find_label_path = False
for label_format in self.label_suffix:
lable_path = os.path.join(self.label_root,
label_path = os.path.join(self.label_root,
img_name + label_format)
if io.exists(lable_path):
if io.exists(label_path):
find_label_path = True
self.label_files.append(lable_path)
self.label_files.append(label_path)
break
if not find_label_path:
logging.warning(
'Not find label file %s for img: %s, skip the sample!' %
(lable_path, img_path))
(label_path, img_path))
self.img_files.remove(img_path)
assert len(self.img_files) == len(self.label_files)

View File

@ -1,5 +1,5 @@
# Copyright (c) OpenMMLab and Alibaba. All rights reserved.
from collections import Sequence
from collections.abc import Sequence
import mmcv
import numpy as np

View File

@ -10,6 +10,7 @@ from .face_keypoint_backbone import FaceKeypointBackbone
from .genet import PlainNet
from .hrnet import HRNet
from .inceptionv3 import Inception3
from .inceptionv4 import Inception4
from .lighthrnet import LiteHRNet
from .mae_vit_transformer import *
from .mit import MixVisionTransformer

View File

@ -1,15 +1,21 @@
# Copyright (c) 2022 Snap Inc. All rights reserved.
import itertools
import os
from distutils.version import LooseVersion
import timm
import torch
import torch.nn as nn
from timm.models.layers import DropPath, trunc_normal_
from timm.models.layers.helpers import to_2tuple
from ..modelzoo import efficientformer as model_urls
from ..registry import BACKBONES
if LooseVersion(timm.__version__) <= LooseVersion('0.8.2'):
from timm.models.layers.helpers import to_2tuple
else:
from timm.layers.helpers import to_2tuple
class Attention(torch.nn.Module):

View File

@ -2,9 +2,6 @@
r""" This model is taken from the official PyTorch model zoo.
- torchvision.models.inception.py on 31th Aug, 2019
"""
from collections import namedtuple
import torch
import torch.nn as nn
import torch.nn.functional as F
@ -16,8 +13,6 @@ from ..registry import BACKBONES
__all__ = ['Inception3']
_InceptionOutputs = namedtuple('InceptionOutputs', ['logits', 'aux_logits'])
@BACKBONES.register_module
class Inception3(nn.Module):
@ -113,6 +108,7 @@ class Inception3(nn.Module):
# N x 768 x 17 x 17
x = self.Mixed_6e(x)
# N x 768 x 17 x 17
aux = None
if self.training and self.aux_logits:
aux = self.AuxLogits(x)
# N x 768 x 17 x 17
@ -132,10 +128,7 @@ class Inception3(nn.Module):
if hasattr(self, 'fc'):
x = self.fc(x)
# N x 1000 (num_classes)
if self.training and self.aux_logits and hasattr(self, 'fc'):
return [_InceptionOutputs(x, aux)]
return [x]
return [aux, x]
class InceptionA(nn.Module):

View File

@ -0,0 +1,393 @@
from __future__ import absolute_import, division, print_function
from collections import namedtuple
import torch
import torch.nn as nn
import torch.nn.functional as F
from mmcv.cnn import constant_init, kaiming_init
from torch.nn.modules.batchnorm import _BatchNorm
from ..modelzoo import inceptionv4 as model_urls
from ..registry import BACKBONES
__all__ = ['Inception4']
class BasicConv2d(nn.Module):
def __init__(self,
in_planes,
out_planes,
kernel_size,
stride=1,
padding=0):
super(BasicConv2d, self).__init__()
self.conv = nn.Conv2d(
in_planes,
out_planes,
kernel_size=kernel_size,
stride=stride,
padding=padding,
bias=False) # verify bias false
self.bn = nn.BatchNorm2d(
out_planes,
eps=0.001, # value found in tensorflow
momentum=0.1, # default pytorch value
affine=True)
self.relu = nn.ReLU(inplace=True)
def forward(self, x):
x = self.conv(x)
x = self.bn(x)
x = self.relu(x)
return x
class Mixed_3a(nn.Module):
def __init__(self):
super(Mixed_3a, self).__init__()
self.maxpool = nn.MaxPool2d(3, stride=2)
self.conv = BasicConv2d(64, 96, kernel_size=3, stride=2)
def forward(self, x):
x0 = self.maxpool(x)
x1 = self.conv(x)
out = torch.cat((x0, x1), 1)
return out
class Mixed_4a(nn.Module):
def __init__(self):
super(Mixed_4a, self).__init__()
self.branch0 = nn.Sequential(
BasicConv2d(160, 64, kernel_size=1, stride=1),
BasicConv2d(64, 96, kernel_size=3, stride=1))
self.branch1 = nn.Sequential(
BasicConv2d(160, 64, kernel_size=1, stride=1),
BasicConv2d(64, 64, kernel_size=(1, 7), stride=1, padding=(0, 3)),
BasicConv2d(64, 64, kernel_size=(7, 1), stride=1, padding=(3, 0)),
BasicConv2d(64, 96, kernel_size=(3, 3), stride=1))
def forward(self, x):
x0 = self.branch0(x)
x1 = self.branch1(x)
out = torch.cat((x0, x1), 1)
return out
class Mixed_5a(nn.Module):
def __init__(self):
super(Mixed_5a, self).__init__()
self.conv = BasicConv2d(192, 192, kernel_size=3, stride=2)
self.maxpool = nn.MaxPool2d(3, stride=2)
def forward(self, x):
x0 = self.conv(x)
x1 = self.maxpool(x)
out = torch.cat((x0, x1), 1)
return out
class Inception_A(nn.Module):
def __init__(self):
super(Inception_A, self).__init__()
self.branch0 = BasicConv2d(384, 96, kernel_size=1, stride=1)
self.branch1 = nn.Sequential(
BasicConv2d(384, 64, kernel_size=1, stride=1),
BasicConv2d(64, 96, kernel_size=3, stride=1, padding=1))
self.branch2 = nn.Sequential(
BasicConv2d(384, 64, kernel_size=1, stride=1),
BasicConv2d(64, 96, kernel_size=3, stride=1, padding=1),
BasicConv2d(96, 96, kernel_size=3, stride=1, padding=1))
self.branch3 = nn.Sequential(
nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False),
BasicConv2d(384, 96, kernel_size=1, stride=1))
def forward(self, x):
x0 = self.branch0(x)
x1 = self.branch1(x)
x2 = self.branch2(x)
x3 = self.branch3(x)
out = torch.cat((x0, x1, x2, x3), 1)
return out
class Reduction_A(nn.Module):
def __init__(self):
super(Reduction_A, self).__init__()
self.branch0 = BasicConv2d(384, 384, kernel_size=3, stride=2)
self.branch1 = nn.Sequential(
BasicConv2d(384, 192, kernel_size=1, stride=1),
BasicConv2d(192, 224, kernel_size=3, stride=1, padding=1),
BasicConv2d(224, 256, kernel_size=3, stride=2))
self.branch2 = nn.MaxPool2d(3, stride=2)
def forward(self, x):
x0 = self.branch0(x)
x1 = self.branch1(x)
x2 = self.branch2(x)
out = torch.cat((x0, x1, x2), 1)
return out
class Inception_B(nn.Module):
def __init__(self):
super(Inception_B, self).__init__()
self.branch0 = BasicConv2d(1024, 384, kernel_size=1, stride=1)
self.branch1 = nn.Sequential(
BasicConv2d(1024, 192, kernel_size=1, stride=1),
BasicConv2d(
192, 224, kernel_size=(1, 7), stride=1, padding=(0, 3)),
BasicConv2d(
224, 256, kernel_size=(7, 1), stride=1, padding=(3, 0)))
self.branch2 = nn.Sequential(
BasicConv2d(1024, 192, kernel_size=1, stride=1),
BasicConv2d(
192, 192, kernel_size=(7, 1), stride=1, padding=(3, 0)),
BasicConv2d(
192, 224, kernel_size=(1, 7), stride=1, padding=(0, 3)),
BasicConv2d(
224, 224, kernel_size=(7, 1), stride=1, padding=(3, 0)),
BasicConv2d(
224, 256, kernel_size=(1, 7), stride=1, padding=(0, 3)))
self.branch3 = nn.Sequential(
nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False),
BasicConv2d(1024, 128, kernel_size=1, stride=1))
def forward(self, x):
x0 = self.branch0(x)
x1 = self.branch1(x)
x2 = self.branch2(x)
x3 = self.branch3(x)
out = torch.cat((x0, x1, x2, x3), 1)
return out
class Reduction_B(nn.Module):
def __init__(self):
super(Reduction_B, self).__init__()
self.branch0 = nn.Sequential(
BasicConv2d(1024, 192, kernel_size=1, stride=1),
BasicConv2d(192, 192, kernel_size=3, stride=2))
self.branch1 = nn.Sequential(
BasicConv2d(1024, 256, kernel_size=1, stride=1),
BasicConv2d(
256, 256, kernel_size=(1, 7), stride=1, padding=(0, 3)),
BasicConv2d(
256, 320, kernel_size=(7, 1), stride=1, padding=(3, 0)),
BasicConv2d(320, 320, kernel_size=3, stride=2))
self.branch2 = nn.MaxPool2d(3, stride=2)
def forward(self, x):
x0 = self.branch0(x)
x1 = self.branch1(x)
x2 = self.branch2(x)
out = torch.cat((x0, x1, x2), 1)
return out
class Inception_C(nn.Module):
def __init__(self):
super(Inception_C, self).__init__()
self.branch0 = BasicConv2d(1536, 256, kernel_size=1, stride=1)
self.branch1_0 = BasicConv2d(1536, 384, kernel_size=1, stride=1)
self.branch1_1a = BasicConv2d(
384, 256, kernel_size=(1, 3), stride=1, padding=(0, 1))
self.branch1_1b = BasicConv2d(
384, 256, kernel_size=(3, 1), stride=1, padding=(1, 0))
self.branch2_0 = BasicConv2d(1536, 384, kernel_size=1, stride=1)
self.branch2_1 = BasicConv2d(
384, 448, kernel_size=(3, 1), stride=1, padding=(1, 0))
self.branch2_2 = BasicConv2d(
448, 512, kernel_size=(1, 3), stride=1, padding=(0, 1))
self.branch2_3a = BasicConv2d(
512, 256, kernel_size=(1, 3), stride=1, padding=(0, 1))
self.branch2_3b = BasicConv2d(
512, 256, kernel_size=(3, 1), stride=1, padding=(1, 0))
self.branch3 = nn.Sequential(
nn.AvgPool2d(3, stride=1, padding=1, count_include_pad=False),
BasicConv2d(1536, 256, kernel_size=1, stride=1))
def forward(self, x):
x0 = self.branch0(x)
x1_0 = self.branch1_0(x)
x1_1a = self.branch1_1a(x1_0)
x1_1b = self.branch1_1b(x1_0)
x1 = torch.cat((x1_1a, x1_1b), 1)
x2_0 = self.branch2_0(x)
x2_1 = self.branch2_1(x2_0)
x2_2 = self.branch2_2(x2_1)
x2_3a = self.branch2_3a(x2_2)
x2_3b = self.branch2_3b(x2_2)
x2 = torch.cat((x2_3a, x2_3b), 1)
x3 = self.branch3(x)
out = torch.cat((x0, x1, x2, x3), 1)
return out
class InceptionAux(nn.Module):
def __init__(self, in_channels, num_classes):
super(InceptionAux, self).__init__()
self.conv0 = BasicConv2d(in_channels, 128, kernel_size=1)
self.conv1 = BasicConv2d(128, 768, kernel_size=5)
self.conv1.stddev = 0.01
self.fc = nn.Linear(768, num_classes)
self.fc.stddev = 0.001
def forward(self, x):
# N x 768 x 17 x 17
x = F.avg_pool2d(x, kernel_size=5, stride=3)
# N x 768 x 5 x 5
x = self.conv0(x)
# N x 128 x 5 x 5
x = self.conv1(x)
# N x 768 x 1 x 1
# Adaptive average pooling
x = F.adaptive_avg_pool2d(x, (1, 1))
# N x 768 x 1 x 1
x = torch.flatten(x, 1)
# N x 768
x = self.fc(x)
# N x 1000
return x
# class BasicConv2d(nn.Module):
# def __init__(self, in_channels, out_channels, **kwargs):
# super(BasicConv2d, self).__init__()
# self.conv = nn.Conv2d(in_channels, out_channels, bias=False, **kwargs)
# self.bn = nn.BatchNorm2d(out_channels, eps=0.001)
# def forward(self, x):
# x = self.conv(x)
# x = self.bn(x)
# return F.relu(x, inplace=True)
@BACKBONES.register_module
class Inception4(nn.Module):
"""InceptionV4 backbone.
Args:
num_classes (int): The num_classes of InceptionV4. An extra fc will be used if
"""
def __init__(self,
num_classes: int = 0,
p_dropout=0.2,
aux_logits: bool = True):
super(Inception4, self).__init__()
self.aux_logits = aux_logits
# Modules
self.features = nn.Sequential(
BasicConv2d(3, 32, kernel_size=3, stride=2),
BasicConv2d(32, 32, kernel_size=3, stride=1),
BasicConv2d(32, 64, kernel_size=3, stride=1, padding=1),
Mixed_3a(),
Mixed_4a(),
Mixed_5a(),
Inception_A(),
Inception_A(),
Inception_A(),
Inception_A(),
Reduction_A(), # Mixed_6a
Inception_B(),
Inception_B(),
Inception_B(),
Inception_B(),
Inception_B(),
Inception_B(),
Inception_B(), # Mixed_6h 1024 x 17 x 17
Reduction_B(), # Mixed_7a
Inception_C(),
Inception_C(),
Inception_C())
if aux_logits:
self.AuxLogits = InceptionAux(1024, num_classes)
self.dropout = nn.Dropout(p_dropout)
self.last_linear = None
if num_classes > 0:
self.last_linear = nn.Linear(1536, num_classes)
self.default_pretrained_model_path = model_urls[
self.__class__.__name__]
@property
def fc(self):
return self.last_linear
def init_weights(self):
for m in self.modules():
if isinstance(m, nn.Conv2d) or isinstance(m, nn.Linear):
for m in self.modules():
if isinstance(m, nn.Conv2d):
kaiming_init(m, mode='fan_in', nonlinearity='relu')
elif isinstance(m, (_BatchNorm, nn.GroupNorm)):
constant_init(m, 1)
elif isinstance(m, nn.BatchNorm2d):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, 0)
def logits(self, features):
x = F.adaptive_avg_pool2d(features, output_size=(1, 1))
# x = F.avg_pool2d(features, kernel_size=adaptiveAvgPoolWidth)
x = x.view(x.size(0), -1) # B x 1536
x = self.fc(x)
# B x num_classes
return x
def forward(self, input: torch.Tensor):
"""_summary_
Args:
input (torch.Tensor): A RGB image tensor with shape B x C x H x W
Returns:
torch.Tensor: A feature tensor or a logit tensor when num_classes is 0 (default)
"""
if self.training and self.aux_logits:
x = self.features[:-4](input)
aux = self.AuxLogits(x)
x = self.features[-4:](x)
else:
x = self.features(input)
aux = None
if self.fc is not None:
x = self.logits(x)
return [aux, x]

View File

@ -151,6 +151,21 @@ class Classification(BaseModel):
x = self.backbone(img)
return x
@torch.jit.unused
def forward_onnx(self, img: torch.Tensor) -> Dict[str, torch.Tensor]:
"""
forward_onnx means generate prob from image only support one neck + one head
"""
x = self.forward_backbone(img) # tuple
# if self.neck_num > 0:
if hasattr(self, 'neck_0'):
x = self.neck_0([i for i in x])
out = self.head_0(x)[0].cpu()
out = self.activate_fn(out)
return out
@torch.jit.unused
def forward_train(self, img, gt_labels) -> Dict[str, torch.Tensor]:
"""
@ -290,6 +305,9 @@ class Classification(BaseModel):
return self.forward_test_label(img, gt_labels)
else:
return self.forward_test(img)
elif mode == 'onnx':
return self.forward_onnx(img)
elif mode == 'extract':
rd = self.forward_feature(img)
rv = {}

View File

@ -52,6 +52,22 @@ class YOLOX(BaseModel):
assert model_type in self.param_map, f'invalid model_type for yolox {model_type}, valid ones are {list(self.param_map.keys())}'
if num_classes is not None:
# adapt to previous export model (before easycv0.6.0)
logging.warning(
'Warning: You are now attend to use an old YOLOX model before easycv0.6.0 with key num_classes'
)
head = dict(
type='YOLOXHead',
model_type=model_type,
num_classes=num_classes,
)
# the change of backbone/neck/head only support model_type as 's'
if model_type != 's':
head_type = head.get('type', None)
assert backbone == 'CSPDarknet' and neck_type == 'yolo' and neck_mode == 'all' and head_type == 'YOLOXHead', 'We only support the architecture modification for YOLOX-S.'
self.pretrained = pretrained
in_channels = [256, 512, 1024]
@ -68,19 +84,14 @@ class YOLOX(BaseModel):
asff_channel=asff_channel,
use_att=use_att)
if num_classes is not None:
# adapt to previous export model (before easycv0.6.0)
logging.warning(
'Warning: You are now attend to use an old YOLOX model before easycv0.6.0 with key num_classes'
)
head = dict(
type='YOLOXHead',
model_type=model_type,
num_classes=num_classes,
)
if head is not None:
# head is None for YOLOX-edge to define a special head
# set and check model type in head as the same of yolox
head_model_type = head.get('model_type', None)
if head_model_type is None:
head['model_type'] = model_type
else:
assert model_type == head_model_type, 'Please provide the same model_type of YOLOX in config.'
self.head = build_head(head)
self.num_classes = self.head.num_classes

View File

@ -79,6 +79,12 @@ inceptionv3 = {
'http://pai-vision-data-hz.oss-cn-zhangjiakou.aliyuncs.com/pretrained_models/easycv/inceptionv3/inception_v3.pth',
}
inceptionv4 = {
# Inception v4 ported from http://data.lip6.fr/cadene/pretrainedmodels/inceptionv4-8e4777a0.pth
'Inception4':
'http://pai-vision-data-hz.oss-cn-zhangjiakou.aliyuncs.com/pretrained_models/easycv/inceptionv4/inception_v4.pth',
}
genet = {
'PlainNetnormal':
'http://pai-vision-data-hz.oss-cn-zhangjiakou.aliyuncs.com/pretrained_models/easycv/genet/GENet_normal.pth',

View File

@ -47,7 +47,7 @@ def get_1d_sincos_pos_embed_from_grid(embed_dim, pos):
out: (M, D)
"""
assert embed_dim % 2 == 0
omega = np.arange(embed_dim // 2, dtype=np.float)
omega = np.arange(embed_dim // 2, dtype=np.float32)
omega /= embed_dim / 2.
omega = 1. / 10000**omega # (D/2,)

View File

@ -1,5 +1,7 @@
# Copyright (c) Alibaba, Inc. and its affiliates.
import glob
import math
import os
import numpy as np
import torch
@ -7,11 +9,18 @@ from PIL import Image
from easycv.file import io
from easycv.framework.errors import ValueError
from easycv.utils.checkpoint import load_checkpoint
from easycv.utils.misc import deprecated
from .base import InputProcessor, OutputProcessor, Predictor, PredictorV2
from .builder import PREDICTORS
# onnx specific
def onnx_to_numpy(tensor):
return tensor.detach().cpu().numpy(
) if tensor.requires_grad else tensor.cpu().numpy()
class ClsInputProcessor(InputProcessor):
"""Process inputs for classification models.
@ -146,6 +155,20 @@ class ClassificationPredictor(PredictorV2):
self.pil_input = pil_input
self.label_map_path = label_map_path
if model_path.endswith('onnx'):
self.model_type = 'onnx'
pwd_model = os.path.dirname(model_path)
raw_model = glob.glob(
os.path.join(pwd_model, '*.onnx.config.json'))
if len(raw_model) != 0:
config_file = raw_model[0]
else:
assert len(
raw_model
) == 0, 'Please have a file with the .onnx.config.json extension in your directory'
else:
self.model_type = 'raw'
if self.pil_input:
mode = 'RGB'
super(ClassificationPredictor, self).__init__(
@ -186,6 +209,41 @@ class ClassificationPredictor(PredictorV2):
return ClsOutputProcessor(topk=self.topk, label_map=self.label_map)
def prepare_model(self):
"""Build model from config file by default.
If the model is not loaded from a configuration file, e.g. torch jit model, you need to reimplement it.
"""
if self.model_type == 'raw':
model = self._build_model()
model.to(self.device)
model.eval()
load_checkpoint(model, self.model_path, map_location='cpu')
return model
else:
import onnxruntime
if onnxruntime.get_device() == 'GPU':
onnx_model = onnxruntime.InferenceSession(
self.model_path, providers=['CUDAExecutionProvider'])
else:
onnx_model = onnxruntime.InferenceSession(self.model_path)
return onnx_model
def model_forward(self, inputs):
"""Model forward.
If you need refactor model forward, you need to reimplement it.
"""
with torch.no_grad():
if self.model_type == 'raw':
outputs = self.model(**inputs, mode='test')
else:
outputs = self.model.run(None, {
self.model.get_inputs()[0].name:
onnx_to_numpy(inputs['img'])
})[0]
outputs = dict(prob=torch.from_numpy(outputs))
return outputs
try:
from easy_vision.python.inference.predictor import PredictorInterface

View File

@ -23,6 +23,12 @@ except Exception:
from .interface import PredictorInterface
# 将张量转化为ndarray格式
def onnx_to_numpy(tensor):
return tensor.detach().cpu().numpy(
) if tensor.requires_grad else tensor.cpu().numpy()
class DetInputProcessor(InputProcessor):
def build_processor(self):
@ -349,9 +355,11 @@ class YoloXPredictor(DetectionPredictor):
self.model_type = 'jit'
elif model_path.endswith('blade'):
self.model_type = 'blade'
elif model_path.endswith('onnx'):
self.model_type = 'onnx'
else:
self.model_type = 'raw'
assert self.model_type in ['raw', 'jit', 'blade']
assert self.model_type in ['raw', 'jit', 'blade', 'onnx']
if self.model_type == 'blade' or self.use_trt_efficientnms:
import torch_blade
@ -381,8 +389,16 @@ class YoloXPredictor(DetectionPredictor):
def _build_model(self):
if self.model_type != 'raw':
if self.model_type != 'onnx':
with io.open(self.model_path, 'rb') as infile:
model = torch.jit.load(infile, self.device)
else:
import onnxruntime
if onnxruntime.get_device() == 'GPU':
model = onnxruntime.InferenceSession(
self.model_path, providers=['CUDAExecutionProvider'])
else:
model = onnxruntime.InferenceSession(self.model_path)
else:
from easycv.utils.misc import reparameterize_models
model = super()._build_model()
@ -394,6 +410,7 @@ class YoloXPredictor(DetectionPredictor):
If the model is not loaded from a configuration file, e.g. torch jit model, you need to reimplement it.
"""
model = self._build_model()
if self.model_type != 'onnx':
model.to(self.device)
model.eval()
if self.model_type == 'raw':
@ -406,7 +423,15 @@ class YoloXPredictor(DetectionPredictor):
"""
if self.model_type != 'raw':
with torch.no_grad():
if self.model_type != 'onnx':
outputs = self.model(inputs['img'])
else:
outputs = self.model.run(
None, {
self.model.get_inputs()[0].name:
onnx_to_numpy(inputs['img'])
})[0]
outputs = torch.from_numpy(outputs)
outputs = {'results': outputs} # convert to dict format
else:
outputs = super().model_forward(inputs)

View File

@ -19,6 +19,8 @@ from easycv.utils.config_tools import mmcv_config_fromfile
from easycv.utils.misc import deprecated
from .base import InputProcessor, OutputProcessor, PredictorV2
np.set_printoptions(suppress=True)
def _box2cs(image_size, box):
"""This encodes bbox(x,y,w,h) into (center, scale)
@ -222,11 +224,12 @@ class PoseTopDownInputProcessor(InputProcessor):
bboxes = bboxes[valid_idx]
person_results = [person_results[i] for i in valid_idx]
output_person_info = []
results = []
for person_result in person_results:
box = person_result['bbox'] # x,y,x,y
box = [box[0], box[1], box[2] - box[0], box[3] - box[1]] # x,y,w,h
center, scale = _box2cs(self.cfg.data_cfg['image_size'], box)
box = person_result['bbox'] # x,y,x,y,s
boxc = [box[0], box[1], box[2] - box[0],
box[3] - box[1]] # x,y,w,h
center, scale = _box2cs(self.cfg.data_cfg['image_size'], boxc)
data = {
'image_id':
0,
@ -264,11 +267,10 @@ class PoseTopDownInputProcessor(InputProcessor):
output['img_fields'],
}
box_id += 1
output_person_info.append(data)
data_processor = self.processor(data)
data_processor['bbox'] = box
results.append(data_processor)
results = []
for output in output_person_info:
results.append(self.processor(output))
return results
def __call__(self, inputs):
@ -296,12 +298,7 @@ class PoseTopDownOutputProcessor(OutputProcessor):
def __call__(self, inputs):
output = {}
output['keypoints'] = inputs['preds']
output['bbox'] = inputs['boxes'] # c1, c2, s1, s2, area, core
for i, bbox in enumerate(output['bbox']):
center, scale = bbox[:2], bbox[2:4]
output['bbox'][i][:4] = bbox_cs2xyxy(center, scale)
output['bbox'] = output['bbox'][:, [0, 1, 2, 3, 5]]
output['bbox'] = np.array(inputs['boxes']) # x1, y1, x2, y2 score
return output
@ -403,6 +400,7 @@ class PoseTopDownPredictor(PredictorV2):
return model
def model_forward(self, inputs, return_heatmap=False):
boxes = inputs['bbox'].cpu().numpy()
if self.model_type == 'raw':
with torch.no_grad():
result = self.model(
@ -423,6 +421,7 @@ class PoseTopDownPredictor(PredictorV2):
result = decode_heatmap(output_heatmap, img_metas,
self.cfg.model.test_cfg)
result['boxes'] = np.array(boxes)
return result
def get_input_processor(self):

View File

@ -38,7 +38,7 @@ class STrack(BaseTrack):
def __init__(self, tlwh, score):
# wait activate
self._tlwh = np.asarray(tlwh, dtype=np.float)
self._tlwh = np.asarray(tlwh, dtype=np.float32)
self.kalman_filter = None
self.mean, self.covariance = None, None
self.is_activated = False

View File

@ -86,15 +86,15 @@ def ious(atlbrs, btlbrs):
:rtype ious np.ndarray
"""
ious = np.zeros((len(atlbrs), len(btlbrs)), dtype=np.float)
ious = np.zeros((len(atlbrs), len(btlbrs)), dtype=np.float32)
if ious.size == 0:
return ious
from cython_bbox import bbox_overlaps as bbox_ious
ious = bbox_ious(
np.ascontiguousarray(atlbrs, dtype=np.float),
np.ascontiguousarray(btlbrs, dtype=np.float))
np.ascontiguousarray(atlbrs, dtype=np.float32),
np.ascontiguousarray(btlbrs, dtype=np.float32))
return ious
@ -151,15 +151,15 @@ def embedding_distance(tracks, detections, metric='cosine'):
:return: cost_matrix np.ndarray
"""
cost_matrix = np.zeros((len(tracks), len(detections)), dtype=np.float)
cost_matrix = np.zeros((len(tracks), len(detections)), dtype=np.float32)
if cost_matrix.size == 0:
return cost_matrix
det_features = np.asarray([track.curr_feat for track in detections],
dtype=np.float)
dtype=np.float32)
#for i, track in enumerate(tracks):
#cost_matrix[i, :] = np.maximum(0.0, cdist(track.smooth_feat.reshape(1,-1), det_features, metric))
track_features = np.asarray([track.smooth_feat for track in tracks],
dtype=np.float)
dtype=np.float32)
cost_matrix = np.maximum(0.0, cdist(track_features, det_features,
metric)) # Nomalized features
return cost_matrix

View File

@ -48,9 +48,15 @@ class EasyCVDetectionPipeline(EasyCVPipeline):
labels = []
boxes = []
for output in outputs:
for score, label, box in zip(output['detection_scores'],
output['detection_classes'],
output['detection_boxes']):
scores_list = output['detection_scores'] if output[
'detection_scores'] is not None else []
classes_list = output['detection_classes'] if output[
'detection_classes'] is not None else []
boxes_list = output['detection_boxes'] if output[
'detection_boxes'] is not None else []
for score, label, box in zip(scores_list, classes_list,
boxes_list):
scores.append(score)
labels.append(self.cfg.CLASSES[label])
boxes.append([b for b in box])

View File

@ -515,6 +515,10 @@ CONFIG_TEMPLATE_ZOO = {
'configs/classification/imagenet/swint/imagenet_swin_tiny_patch4_window7_224_jpg.py',
'CLASSIFICATION_M0BILENET':
'configs/classification/imagenet/mobilenet/mobilenetv2.py',
'CLASSIFICATION_INCEPTIONV4':
'configs/classification/imagenet/inception/inceptionv4_b32x8_100e.py',
'CLASSIFICATION_INCEPTIONV3':
'configs/classification/imagenet/inception/inceptionv3_b32x8_100e.py',
# metric learning
'METRICLEARNING':

View File

@ -2,5 +2,5 @@
# GENERATED VERSION FILE
# TIME: Thu Nov 5 14:17:50 2020
__version__ = '0.11.3'
short_version = '0.11.3'
__version__ = '0.11.7'
short_version = '0.11.7'

View File

@ -13,6 +13,7 @@ lmdb
numba
numpy
nuscenes-devkit
onnxruntime
opencv-python
oss2
packaging

View File

@ -116,6 +116,7 @@ class ModelExportTest(unittest.TestCase):
cfg = mmcv_config_fromfile(config_file)
cfg_options = {
'model.backbone.norm_cfg.type': 'SyncBN',
'export.export_type': 'raw'
}
if cfg_options is not None:
cfg.merge_from_dict(cfg_options)
@ -210,6 +211,27 @@ class ModelExportTest(unittest.TestCase):
self.assertTrue(os.path.exists(filename + '.jit'))
def test_export_resnet_onnx(self):
ckpt_path = PRETRAINED_MODEL_RESNET50
easycv_dir = os.path.dirname(easycv.__file__)
if os.path.exists(os.path.join(easycv_dir, 'configs')):
config_dir = os.path.join(easycv_dir, 'configs')
else:
config_dir = os.path.join(os.path.dirname(easycv_dir), 'configs')
config_file = os.path.join(
config_dir,
'classification/imagenet/resnet/imagenet_resnet50_jpg.py')
with tempfile.TemporaryDirectory() as tmpdir:
cfg = mmcv_config_fromfile(config_file)
cfg.export.export_type = 'onnx'
filename = os.path.join(tmpdir, 'imagenet_resnet50')
export(cfg, ckpt_path, filename)
self.assertTrue(os.path.exists(filename + '.onnx'))
if __name__ == '__main__':
unittest.main()

View File

@ -89,8 +89,8 @@ class CocoToolsTest(unittest.TestCase):
def testExportGroundtruthToCOCO(self):
image_ids = ['first', 'second']
groundtruth_boxes = [
np.array([[100, 100, 200, 200]], np.float),
np.array([[50, 50, 100, 100]], np.float)
np.array([[100, 100, 200, 200]], np.float32),
np.array([[50, 50, 100, 100]], np.float32)
]
groundtruth_classes = [
np.array([1], np.int32),
@ -126,12 +126,12 @@ class CocoToolsTest(unittest.TestCase):
def testExportDetectionsToCOCO(self):
image_ids = ['first', 'second']
detections_boxes = [
np.array([[100, 100, 200, 200]], np.float),
np.array([[50, 50, 100, 100]], np.float)
np.array([[100, 100, 200, 200]], np.float32),
np.array([[50, 50, 100, 100]], np.float32)
]
detections_scores = [
np.array([.8], np.float),
np.array([.7], np.float)
np.array([.8], np.float32),
np.array([.7], np.float32)
]
detections_classes = [np.array([1], np.int32), np.array([1], np.int32)]
categories = [{
@ -152,7 +152,17 @@ class CocoToolsTest(unittest.TestCase):
detections_classes,
categories,
output_path=output_path)
self.assertListEqual(result, self._detections_list)
self.assertEqual(len(result), len(detections_boxes))
self.assertEqual(len(detections_boxes), len(detections_boxes))
score_list = []
for i in range(len(detections_boxes)):
score = self._detections_list[i].pop('score')
score_list.append(score)
self.assertAlmostEqual(result[i].pop('score'), score)
self.assertDictEqual(result[i], self._detections_list[i])
with io.open(output_path, 'r') as f:
written_result = f.read()
# The json output should have floats written to 4 digits of precision.
@ -160,7 +170,10 @@ class CocoToolsTest(unittest.TestCase):
re.MULTILINE)
self.assertTrue(matcher.findall(written_result))
written_result = json.loads(written_result)
self.assertAlmostEqual(result, written_result)
for i in range(len(result)):
self.assertAlmostEqual(written_result[i].pop('score'),
score_list[i])
self.assertDictEqual(result[i], written_result[i])
def testExportSegmentsToCOCO(self):
image_ids = ['first', 'second']
@ -176,7 +189,10 @@ class CocoToolsTest(unittest.TestCase):
for i, detection_mask in enumerate(detection_masks):
detection_masks[i] = detection_mask[:, :, :, None]
detection_scores = [np.array([.8], np.float), np.array([.7], np.float)]
detection_scores = [
np.array([.8], np.float32),
np.array([.7], np.float32)
]
detection_classes = [np.array([1], np.int32), np.array([1], np.int32)]
categories = [{
@ -202,7 +218,12 @@ class CocoToolsTest(unittest.TestCase):
written_result = json.loads(written_result)
mask_load = mask.decode([written_result[0]['segmentation']])
self.assertTrue(np.allclose(mask_load, detection_masks[0]))
self.assertAlmostEqual(result, written_result)
self.assertEqual(len(result), len(detection_masks))
self.assertEqual(len(written_result), len(detection_masks))
for i in range(len(detection_masks)):
self.assertAlmostEqual(result[i].pop('score'),
written_result[i].pop('score'))
self.assertDictEqual(result[i], written_result[i])
def testExportKeypointsToCOCO(self):
image_ids = ['first', 'second']
@ -216,8 +237,8 @@ class CocoToolsTest(unittest.TestCase):
]
detection_scores = [
np.array([.8, 0.2], np.float),
np.array([.7, 0.3], np.float)
np.array([.8, 0.2], np.float32),
np.array([.7, 0.3], np.float32)
]
detection_classes = [
np.array([1, 1], np.int32),
@ -248,7 +269,12 @@ class CocoToolsTest(unittest.TestCase):
with io.open(output_path, 'r') as f:
written_result = f.read()
written_result = json.loads(written_result)
self.assertAlmostEqual(result, written_result)
self.assertEqual(len(result), 4)
self.assertEqual(len(written_result), 4)
for i in range(4):
self.assertAlmostEqual(result[i].pop('score'),
written_result[i].pop('score'))
self.assertDictEqual(result[i], written_result[i])
def testSingleImageDetectionBoxesExport(self):
boxes = np.array([[0, 0, 1, 1], [0, 0, .5, .5], [.5, .5, 1, 1]],

View File

@ -0,0 +1,63 @@
# Copyright (c) Alibaba, Inc. and its affiliates.
import copy
import random
import unittest
import numpy as np
import torch
from easycv.models import modelzoo
from easycv.models.backbones import Inception4
class InceptionV3Test(unittest.TestCase):
def setUp(self):
print(('Testing %s.%s' % (type(self).__name__, self._testMethodName)))
def test_inceptionv3_withfc(self):
with torch.no_grad():
# input data
batch_size = random.randint(10, 30)
a = torch.rand(batch_size, 3, 299, 299).to('cuda')
num_classes = random.randint(10, 1000)
net = Inception4(
aux_logits=True, num_classes=num_classes).to('cuda')
net.init_weights()
net.train()
self.assertTrue(len(list(net(a)[-1].shape)) == 2)
self.assertTrue(len(list(net(a)[0].shape)) == 2)
self.assertTrue(net(a)[-1].size(1) == num_classes)
self.assertTrue(net(a)[-1].size(0) == batch_size)
self.assertTrue(net(a)[0].size(1) == num_classes)
self.assertTrue(net(a)[0].size(0) == batch_size)
def test_inceptionv3_withoutfc(self):
with torch.no_grad():
# input data
batch_size = random.randint(10, 30)
a = torch.rand(batch_size, 3, 299, 299).to('cuda')
net = Inception4(aux_logits=True, num_classes=0).to('cuda')
net.init_weights()
net.eval()
self.assertTrue(net(a)[-1].size(1) == 1536)
self.assertTrue(net(a)[-1].size(0) == batch_size)
def test_inceptionv3_load_modelzoo(self):
with torch.no_grad():
net = Inception4(aux_logits=True, num_classes=1000).to('cuda')
original_weight = net.features[0].conv.weight
original_weight = copy.deepcopy(original_weight.cpu().data.numpy())
net.init_weights()
load_weight = net.features[0].conv.weight.cpu().data.numpy()
self.assertFalse(np.allclose(original_weight, load_weight))
if __name__ == '__main__':
unittest.main()

View File

@ -40,9 +40,9 @@ class YOLOXTest(unittest.TestCase):
}
output = model(imgs, mode='train', **kwargs)
self.assertEqual(output['img_h'].cpu().numpy(),
np.array(640, dtype=np.float))
np.array(640, dtype=np.float32))
self.assertEqual(output['img_w'].cpu().numpy(),
np.array(640, dtype=np.float))
np.array(640, dtype=np.float32))
self.assertEqual(output['total_loss'].shape, torch.Size([]))
self.assertEqual(output['iou_l'].shape, torch.Size([]))
self.assertEqual(output['conf_l'].shape, torch.Size([]))

View File

@ -45,9 +45,9 @@ class YOLOXEDGETest(unittest.TestCase):
}
output = model(imgs, mode='train', **kwargs)
self.assertEqual(output['img_h'].cpu().numpy(),
np.array(640, dtype=np.float))
np.array(640, dtype=np.float32))
self.assertEqual(output['img_w'].cpu().numpy(),
np.array(640, dtype=np.float))
np.array(640, dtype=np.float32))
self.assertEqual(output['total_loss'].shape, torch.Size([]))
self.assertEqual(output['iou_l'].shape, torch.Size([]))
self.assertEqual(output['conf_l'].shape, torch.Size([]))

View File

@ -11,7 +11,8 @@ import torch
from easycv.predictors.classifier import ClassificationPredictor
from easycv.utils.test_util import clean_up, get_tmp_dir
from tests.ut_config import (PRETRAINED_MODEL_RESNET50_WITHOUTHEAD,
IMAGENET_LABEL_TXT, TEST_IMAGES_DIR)
IMAGENET_LABEL_TXT, TEST_IMAGES_DIR,
PRETRAINED_MODEL_RESNET50_ONNX_WITHOUTHEAD)
class ClassificationPredictorTest(unittest.TestCase):
@ -33,6 +34,17 @@ class ClassificationPredictorTest(unittest.TestCase):
self.assertListEqual(results['class_name'], ['"Persian cat",'])
self.assertEqual(len(results['class_probs']), 1000)
def test_onnx_single(self):
checkpoint = PRETRAINED_MODEL_RESNET50_ONNX_WITHOUTHEAD
predict_op = ClassificationPredictor(model_path=checkpoint)
img_path = os.path.join(TEST_IMAGES_DIR, 'catb.jpg')
results = predict_op([img_path])[0]
self.assertListEqual(results['class'], [578])
self.assertListEqual(results['class_name'], ['gown'])
self.assertEqual(len(results['class_probs']), 1000)
def test_batch(self):
checkpoint = PRETRAINED_MODEL_RESNET50_WITHOUTHEAD
config_file = 'configs/classification/imagenet/resnet/imagenet_resnet50_jpg.py'

View File

@ -54,10 +54,10 @@ class PoseTopDownPredictorTest(unittest.TestCase):
assert_array_almost_equal(
result0['bbox'],
np.array([[352.3085, 59.00325, 691.4247, 511.15814, 1.],
[10.511196, 177.74883, 101.824326, 299.49966, 1.],
[224.82036, 114.439865, 312.51306, 231.36348, 1.],
[200.71407, 114.716736, 337.17535, 296.6651, 1.]],
np.array([[438.9, 59., 604.8, 511.2, 0.9],
[10.5, 179.6, 101.8, 297.7, 0.9],
[229.6, 114.4, 307.8, 231.4, 0.6],
[229.4, 114.7, 308.5, 296.7, 0.6]],
dtype=np.float32),
decimal=1)
vis_result = predictor.show_result(img1, result0)
@ -92,10 +92,10 @@ class PoseTopDownPredictorTest(unittest.TestCase):
assert_array_almost_equal(
result1['bbox'][:4],
np.array([[436.23096, 214.72766, 584.26013, 412.09985, 1.],
[43.990044, 91.04126, 164.28406, 251.43329, 1.],
[127.44148, 100.38604, 254.219, 269.42273, 1.],
[190.08075, 117.31801, 311.22394, 278.8423, 1.]],
np.array([[470.6, 214.7, 549.9, 412.1, 0.9],
[71.6, 91., 136.7, 251.4, 0.9],
[159.7, 100.4, 221.9, 269.4, 0.9],
[219.4, 117.3, 281.9, 278.8, 0.9]],
dtype=np.float32),
decimal=1)
vis_result = predictor.show_result(img2, result1)

View File

@ -8,13 +8,11 @@ from modelscope.outputs import OutputKeys
from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks
from modelscope.utils.cv.image_utils import panoptic_seg_masks_to_image
from modelscope.utils.demo_utils import DemoCompatibilityCheck
from modelscope.utils.test_utils import test_level
from tests.ut_config import BASE_LOCAL_PATH
class EasyCVPanopticSegmentationPipelineTest(unittest.TestCase,
DemoCompatibilityCheck):
class EasyCVPanopticSegmentationPipelineTest(unittest.TestCase):
img_path = os.path.join(
BASE_LOCAL_PATH, 'data/test_images/image_semantic_segmentation.jpg')
@ -32,10 +30,6 @@ class EasyCVPanopticSegmentationPipelineTest(unittest.TestCase,
cv2.imwrite(tmp_save_path, draw_img)
print('print ' + self.model_id + ' success')
@unittest.skipUnless(test_level() >= 0, 'skip test in current test level')
def test_demo_compatibility(self):
self.compatibility_check()
if __name__ == '__main__':
unittest.main()

View File

@ -9,14 +9,12 @@ from modelscope.outputs import OutputKeys
from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks
from modelscope.utils.cv.image_utils import semantic_seg_masks_to_image
from modelscope.utils.demo_utils import DemoCompatibilityCheck
from modelscope.utils.test_utils import test_level
from PIL import Image
from tests.ut_config import BASE_LOCAL_PATH
class EasyCVSegmentationPipelineTest(unittest.TestCase,
DemoCompatibilityCheck):
class EasyCVSegmentationPipelineTest(unittest.TestCase):
img_path = os.path.join(BASE_LOCAL_PATH,
'data/test_images/image_segmentation.jpg')
@ -82,10 +80,6 @@ class EasyCVSegmentationPipelineTest(unittest.TestCase,
model_id = 'damo/cv_segformer-b5_image_semantic-segmentation_coco-stuff164k'
self._internal_test_(model_id)
@unittest.skipUnless(test_level() >= 0, 'skip test in current test level')
def test_demo_compatibility(self):
self.compatibility_check()
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,138 @@
# Copyright (c) Alibaba, Inc. and its affiliates.
import logging
import os
import sys
import unittest
import numpy as np
import onnxruntime
import torch
from easycv.models import build_model
from easycv.utils.checkpoint import load_checkpoint
from easycv.utils.config_tools import mmcv_config_fromfile, rebuild_config
from easycv.utils.test_util import run_in_subprocess
sys.path.append(os.path.dirname(os.path.realpath(__file__)))
logging.basicConfig(level=logging.INFO)
WORK_DIRECTORY = 'work_dir3'
BASIC_EXPORT_CONFIGS = {
'config_file': None,
'checkpoint': 'dummy',
'output_filename': f'{WORK_DIRECTORY}/test_out.pth',
'user_config_params': ['--export.export_type', 'onnx']
}
def build_cmd(export_configs, MODEL_TYPE) -> str:
base_cmd = 'python tools/export.py'
base_cmd += f" {export_configs['config_file']}"
base_cmd += f" {export_configs['checkpoint']}"
base_cmd += f" {export_configs['output_filename']}"
base_cmd += f' --model_type {MODEL_TYPE}'
user_params = ' '.join(export_configs['user_config_params'])
base_cmd += f' --user_config_params {user_params}'
return base_cmd
class ExportTest(unittest.TestCase):
"""In this unittest, we test the onnx export functionality of
some classification/detection models.
"""
def setUp(self):
print(('Testing %s.%s' % (type(self).__name__, self._testMethodName)))
os.makedirs(WORK_DIRECTORY, exist_ok=True)
def tearDown(self):
super().tearDown()
def run_test(self,
CONFIG_FILE,
MODEL_TYPE,
img_size: int = 224,
**override_configs):
configs = BASIC_EXPORT_CONFIGS.copy()
configs['config_file'] = CONFIG_FILE
configs.update(override_configs)
cmd = build_cmd(configs, MODEL_TYPE)
logging.info(f'Export with commands: {cmd}')
run_in_subprocess(cmd)
cfg = mmcv_config_fromfile(configs['config_file'])
cfg = rebuild_config(cfg, configs['user_config_params'])
if hasattr(cfg.model, 'pretrained'):
cfg.model.pretrained = False
torch_model = build_model(cfg.model).eval()
if 'checkpoint' in override_configs:
load_checkpoint(
torch_model,
override_configs['checkpoint'],
strict=False,
logger=logging.getLogger())
session = onnxruntime.InferenceSession(configs['output_filename'] +
'.onnx')
input_tensor = torch.randn((1, 3, img_size, img_size))
torch_output = torch_model(input_tensor, mode='test')['prob']
onnx_output = session.run(
[session.get_outputs()[0].name],
{session.get_inputs()[0].name: np.array(input_tensor)})
if isinstance(onnx_output, list):
onnx_output = onnx_output[0]
onnx_output = torch.tensor(onnx_output)
is_same_shape = torch_output.shape == onnx_output.shape
self.assertTrue(
is_same_shape,
f'The shapes of the two outputs are mismatch, got {torch_output.shape} and {onnx_output.shape}'
)
is_allclose = torch.allclose(torch_output, onnx_output)
torch_out_minmax = f'{float(torch_output.min())}~{float(torch_output.max())}'
onnx_out_minmax = f'{float(onnx_output.min())}~{float(onnx_output.max())}'
info_msg = f'got avg: {float(torch_output.mean())} and {float(onnx_output.mean())},'
info_msg += f' and range: {torch_out_minmax} and {onnx_out_minmax}'
self.assertTrue(
is_allclose,
f'The values between the two outputs are mismatch, {info_msg}')
def test_inceptionv3(self):
CONFIG_FILE = 'configs/classification/imagenet/inception/inceptionv3_b32x8_100e.py'
self.run_test(CONFIG_FILE, 'CLASSIFICATION_INCEPTIONV3', 299)
def test_inceptionv4(self):
CONFIG_FILE = 'configs/classification/imagenet/inception/inceptionv4_b32x8_100e.py'
self.run_test(CONFIG_FILE, 'CLASSIFICATION_INCEPTIONV4', 299)
def test_resnext50(self):
CONFIG_FILE = 'configs/classification/imagenet/resnext/imagenet_resnext50-32x4d_jpg.py'
self.run_test(
CONFIG_FILE,
'CLASSIFICATION_RESNEXT',
checkpoint=
'https://pai-vision-data-hz.oss-cn-zhangjiakou.aliyuncs.com/EasyCV/modelzoo/classification/resnext/resnext50-32x4d/epoch_100.pth'
)
def test_mobilenetv2(self):
CONFIG_FILE = 'configs/classification/imagenet/mobilenet/mobilenetv2.py'
self.run_test(
CONFIG_FILE,
'CLASSIFICATION_M0BILENET',
checkpoint=
'http://pai-vision-data-hz.oss-cn-zhangjiakou.aliyuncs.com/pretrained_models/easycv/mobilenetv2/mobilenet_v2.pth'
)
if __name__ == '__main__':
unittest.main()

View File

@ -83,12 +83,12 @@ class PredictTest(unittest.TestCase):
oss_config = get_oss_config()
ak_id = oss_config['ak_id']
ak_secret = oss_config['ak_secret']
hosts = oss_config['hosts'] + ['oss-cn-hangzhou.aliyuncs.com']
hosts = oss_config['hosts']
hosts = ','.join(_ for _ in hosts)
buckets = oss_config['buckets'] + ['easycv']
buckets = oss_config['buckets']
buckets = ','.join(_ for _ in buckets)
input_file = 'oss://easycv/data/small_test_data/test_images/http_image_list.txt'
input_file = 'oss://pai-vision-data-hz/unittest/local_backup/easycv_nfs/data/test_images/http_image_list.txt'
output_file = tempfile.NamedTemporaryFile('w').name
cmd = f'PYTHONPATH=. python tools/predict.py \
--input_file {input_file} \

View File

@ -179,6 +179,9 @@ PRETRAINED_MODEL_RESNET50 = os.path.join(
PRETRAINED_MODEL_RESNET50_WITHOUTHEAD = os.path.join(
BASE_LOCAL_PATH,
'pretrained_models/classification/resnet/resnet50_withhead.pth')
PRETRAINED_MODEL_RESNET50_ONNX_WITHOUTHEAD = os.path.join(
BASE_LOCAL_PATH,
'pretrained_models/classification/resnet/imagenet_resnet50.onnx')
PRETRAINED_MODEL_FACEID = os.path.join(BASE_LOCAL_PATH,
'pretrained_models/faceid')
PRETRAINED_MODEL_YOLOXS_EXPORT = os.path.join(