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 ## 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). Please refer to [data_hub.md](docs/source/data_hub.md).

View File

@ -4,16 +4,31 @@ num_classes = 1000
# model settings # model settings
model = dict( model = dict(
type='Classification', type='Classification',
backbone=dict(type='Inception3'), backbone=dict(type='Inception3', num_classes=1000),
head=dict( head=[
type='ClsHead', dict(
with_avg_pool=True, type='ClsHead',
in_channels=2048, with_fc=False,
loss_config=dict( in_channels=2048,
type='CrossEntropyLossWithLabelSmooth', loss_config=dict(
label_smooth=0, type='CrossEntropyLossWithLabelSmooth',
label_smooth=0,
),
num_classes=num_classes,
input_feature_index=[1],
), ),
num_classes=num_classes)) 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 = [ class_list = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13',
@ -196,3 +211,5 @@ log_config = dict(
interval=10, interval=10,
hooks=[dict(type='TextLoggerHook'), hooks=[dict(type='TextLoggerHook'),
dict(type='TensorboardLoggerHook')]) 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', type='CrossEntropyLossWithLabelSmooth',
label_smooth=0, label_smooth=0,
), ),
num_classes=num_classes)) num_classes=num_classes),
pretrained=True)
# optimizer # optimizer
optimizer = dict(type='SGD', lr=0.1, momentum=0.9, weight_decay=0.0001) 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 # runtime settings
total_epochs = 100 total_epochs = 100
checkpoint_sync_export = True 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, depth=50,
out_indices=[4], # 0: conv-1, x: stage-x out_indices=[4], # 0: conv-1, x: stage-x
norm_cfg=dict(type='BN'))) 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', type='CrossEntropyLossWithLabelSmooth',
label_smooth=0, label_smooth=0,
), ),
num_classes=num_classes)) num_classes=num_classes),
pretrained=True)
# optimizer # optimizer
optimizer = dict(type='SGD', lr=0.1, momentum=0.9, weight_decay=0.0001) 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 # runtime settings
total_epochs = 100 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) mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
train_pipeline = [ 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( dict(
type='MMRandomAffine', type='MMRandomAffine',
scaling_ratio_range='${scale_ratio}', scaling_ratio_range=scale_ratio,
border=['-${img_scale}[0] // 2', '-${img_scale}[1] // 2']), border=[img_scale[0] // 2, img_scale[1] // 2]),
dict( dict(
type='MMMixUp', # s m x l; tiny nano will detele 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), ratio_range=(0.8, 1.6),
pad_val=114.0), pad_val=114.0),
dict( dict(
@ -70,45 +70,43 @@ train_pipeline = [
dict(type='MMPad', pad_to_square=True, pad_val=(114.0, 114.0, 114.0)), dict(type='MMPad', pad_to_square=True, pad_val=(114.0, 114.0, 114.0)),
dict( dict(
type='MMNormalize', type='MMNormalize',
mean='${img_norm_cfg.mean}', mean=img_norm_cfg['mean'],
std='${img_norm_cfg.std}', std=img_norm_cfg['std'],
to_rgb='${img_norm_cfg.to_rgb}'), to_rgb=img_norm_cfg['to_rgb']),
dict(type='DefaultFormatBundle'), dict(type='DefaultFormatBundle'),
dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels']) dict(type='Collect', keys=['img', 'gt_bboxes', 'gt_labels'])
] ]
test_pipeline = [ 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='MMPad', pad_to_square=True, pad_val=(114.0, 114.0, 114.0)),
dict( dict(
type='MMNormalize', type='MMNormalize',
mean='${img_norm_cfg.mean}', mean=img_norm_cfg['mean'],
std='${img_norm_cfg.std}', std=img_norm_cfg['std'],
to_rgb='${img_norm_cfg.to_rgb}'), to_rgb=img_norm_cfg['to_rgb']),
dict(type='DefaultFormatBundle'), dict(type='DefaultFormatBundle'),
dict(type='Collect', keys=['img']) dict(type='Collect', keys=['img'])
] ]
train_path = 'data/coco/train2017.manifest'
val_path = 'data/coco/val2017.manifest'
train_dataset = dict(
type='DetImagesMixDataset',
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=val_path, classes=CLASSES),
pipeline=test_pipeline,
dynamic_scale=None,
label_padding=False)
data = dict( data = dict(
imgs_per_gpu=16, imgs_per_gpu=16, workers_per_gpu=4, train=train_dataset, val=val_dataset)
workers_per_gpu=4,
train=dict(
type='DetImagesMixDataset',
data_source=dict(
type='DetSourcePAI',
path='data/coco/train2017.manifest',
classes='${CLASSES}'),
pipeline='${train_pipeline}',
dynamic_scale='${img_scale}'),
val=dict(
type='DetImagesMixDataset',
imgs_per_gpu=2,
data_source=dict(
type='DetSourcePAI',
path='data/coco/val2017.manifest',
classes='${CLASSES}'),
pipeline='${test_pipeline}',
dynamic_scale=None,
label_padding=False))
# additional hooks # additional hooks
interval = 10 interval = 10
@ -120,14 +118,14 @@ custom_hooks = [
priority=48), priority=48),
dict( dict(
type='SyncRandomSizeHook', type='SyncRandomSizeHook',
ratio_range='${random_size}', ratio_range=random_size,
img_scale='${img_scale}', img_scale=img_scale,
interval='${interval}', interval=interval,
priority=48), priority=48),
dict( dict(
type='SyncNormHook', type='SyncNormHook',
num_last_epochs=15, num_last_epochs=15,
interval='${interval}', interval=interval,
priority=48) priority=48)
] ]
@ -135,23 +133,23 @@ custom_hooks = [
vis_num = 20 vis_num = 20
score_thr = 0.5 score_thr = 0.5
eval_config = dict( eval_config = dict(
interval='${interval}', interval=interval,
gpu_collect=False, gpu_collect=False,
visualization_config=dict( visualization_config=dict(
vis_num='${vis_num}', vis_num=vis_num,
score_thr='${score_thr}', score_thr=score_thr,
) # show by TensorboardLoggerHookV2 ) # show by TensorboardLoggerHookV2
) )
eval_pipelines = [ eval_pipelines = [
dict( dict(
mode='test', mode='test',
data='${data.val}', data=val_dataset,
evaluators=[dict(type='CocoDetectionEvaluator', classes=CLASSES)], evaluators=[dict(type='CocoDetectionEvaluator', classes=CLASSES)],
) )
] ]
checkpoint_config = dict(interval='${interval}') checkpoint_config = dict(interval=interval)
# optimizer # optimizer
# basic_lr_per_img = 0.01 / 64.0 # basic_lr_per_img = 0.01 / 64.0
optimizer = dict( optimizer = dict(

View File

@ -92,13 +92,13 @@
### Verification ### Verification
Simple verification Simple verification
```python ```python
from easycv.apis import * from easycv.apis import *
``` ```
You can also verify your installation using following quick-start examples You can also verify your installation using following quick-start examples
## Examples ## Examples

View File

@ -157,6 +157,52 @@ def _export_jit_and_blade(model, cfg, filename, dummy_inputs, fp16=False):
torch.jit.save(blade_model, ofile) 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): def _export_cls(model, cfg, filename):
""" export cls (cls & metric learning)model and preprocess config """ export cls (cls & metric learning)model and preprocess config
@ -170,6 +216,7 @@ def _export_cls(model, cfg, filename):
else: else:
export_cfg = dict(export_neck=False) export_cfg = dict(export_neck=False)
export_type = export_cfg.get('export_type', 'raw')
export_neck = export_cfg.get('export_neck', True) export_neck = export_cfg.get('export_neck', True)
label_map_path = cfg.get('label_map_path', None) label_map_path = cfg.get('label_map_path', None)
class_list = 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')): if export_neck and (k.startswith('neck') or k.startswith('head')):
state_dict[k] = v state_dict[k] = v
checkpoint = dict(state_dict=state_dict, meta=meta, author='EasyCV') if export_type == 'raw':
with io.open(filename, 'wb') as ofile: checkpoint = dict(state_dict=state_dict, meta=meta, author='EasyCV')
torch.save(checkpoint, ofile) 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): def _export_yolox(model, cfg, filename):
@ -247,10 +299,10 @@ def _export_yolox(model, cfg, filename):
if hasattr(cfg, 'export'): if hasattr(cfg, 'export'):
export_type = getattr(cfg.export, 'export_type', 'raw') 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: if export_type not in default_export_type_list:
logging.warning( 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' export_type = 'raw'
@ -276,7 +328,7 @@ def _export_yolox(model, cfg, filename):
len(img_scale) == 2 len(img_scale) == 2
), 'Export YoloX predictor config contains img_scale must be (int, int) tuple!' ), '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 # assert use_trt_efficientnms only happens when static_opt=True
if static_opt is not True: if static_opt is not True:
@ -355,6 +407,31 @@ def _export_yolox(model, cfg, filename):
json.dump(config, ofile) 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': if export_type == 'jit':
with io.open(filename + '.jit', 'wb') as ofile: with io.open(filename + '.jit', 'wb') as ofile:
torch.jit.save(yolox_trace, 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) model.to(device)
if hasattr(cfg, 'export') and getattr(cfg.export, 'type', 'raw') == 'raw': 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) return _export_common(model, cfg, filename)
def _dummy_inputs(cfg): def _dummy_inputs(cfg):

View File

@ -467,8 +467,8 @@ class COCOeval:
fps = np.logical_and( fps = np.logical_and(
np.logical_not(dtm), np.logical_not(dtIg)) np.logical_not(dtm), np.logical_not(dtIg))
tp_sum = np.cumsum(tps, 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.float) fp_sum = np.cumsum(fps, axis=1).astype(dtype=np.float32)
for t, (tp, fp) in enumerate(zip(tp_sum, fp_sum)): for t, (tp, fp) in enumerate(zip(tp_sum, fp_sum)):
tp = np.array(tp) tp = np.array(tp)
fp = np.array(fp) 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_list = [0.6, 0.8, 1.0, 1.2, 1.4]
skin_factor = np.random.choice(skin_factor_list) skin_factor = np.random.choice(skin_factor_list)
img_ycrcb_raw[:, :, 0:1] = np.clip( 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) 255).astype(np.uint8)
img = cv2.cvtColor(img_ycrcb_raw, cv2.COLOR_YCR_CB2BGR) img = cv2.cvtColor(img_ycrcb_raw, cv2.COLOR_YCR_CB2BGR)

View File

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

View File

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

View File

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

View File

@ -1,15 +1,21 @@
# Copyright (c) 2022 Snap Inc. All rights reserved. # Copyright (c) 2022 Snap Inc. All rights reserved.
import itertools import itertools
import os import os
from distutils.version import LooseVersion
import timm
import torch import torch
import torch.nn as nn import torch.nn as nn
from timm.models.layers import DropPath, trunc_normal_ from timm.models.layers import DropPath, trunc_normal_
from timm.models.layers.helpers import to_2tuple
from ..modelzoo import efficientformer as model_urls from ..modelzoo import efficientformer as model_urls
from ..registry import BACKBONES 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): class Attention(torch.nn.Module):

View File

@ -2,9 +2,6 @@
r""" This model is taken from the official PyTorch model zoo. r""" This model is taken from the official PyTorch model zoo.
- torchvision.models.inception.py on 31th Aug, 2019 - torchvision.models.inception.py on 31th Aug, 2019
""" """
from collections import namedtuple
import torch import torch
import torch.nn as nn import torch.nn as nn
import torch.nn.functional as F import torch.nn.functional as F
@ -16,8 +13,6 @@ from ..registry import BACKBONES
__all__ = ['Inception3'] __all__ = ['Inception3']
_InceptionOutputs = namedtuple('InceptionOutputs', ['logits', 'aux_logits'])
@BACKBONES.register_module @BACKBONES.register_module
class Inception3(nn.Module): class Inception3(nn.Module):
@ -113,6 +108,7 @@ class Inception3(nn.Module):
# N x 768 x 17 x 17 # N x 768 x 17 x 17
x = self.Mixed_6e(x) x = self.Mixed_6e(x)
# N x 768 x 17 x 17 # N x 768 x 17 x 17
aux = None
if self.training and self.aux_logits: if self.training and self.aux_logits:
aux = self.AuxLogits(x) aux = self.AuxLogits(x)
# N x 768 x 17 x 17 # N x 768 x 17 x 17
@ -132,10 +128,7 @@ class Inception3(nn.Module):
if hasattr(self, 'fc'): if hasattr(self, 'fc'):
x = self.fc(x) x = self.fc(x)
# N x 1000 (num_classes) return [aux, x]
if self.training and self.aux_logits and hasattr(self, 'fc'):
return [_InceptionOutputs(x, aux)]
return [x]
class InceptionA(nn.Module): 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) x = self.backbone(img)
return x 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 @torch.jit.unused
def forward_train(self, img, gt_labels) -> Dict[str, torch.Tensor]: 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) return self.forward_test_label(img, gt_labels)
else: else:
return self.forward_test(img) return self.forward_test(img)
elif mode == 'onnx':
return self.forward_onnx(img)
elif mode == 'extract': elif mode == 'extract':
rd = self.forward_feature(img) rd = self.forward_feature(img)
rv = {} 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())}' 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 self.pretrained = pretrained
in_channels = [256, 512, 1024] in_channels = [256, 512, 1024]
@ -68,19 +84,14 @@ class YOLOX(BaseModel):
asff_channel=asff_channel, asff_channel=asff_channel,
use_att=use_att) 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: if head is not None:
# head is None for YOLOX-edge to define a special head # 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.head = build_head(head)
self.num_classes = self.head.num_classes 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', '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 = { genet = {
'PlainNetnormal': 'PlainNetnormal':
'http://pai-vision-data-hz.oss-cn-zhangjiakou.aliyuncs.com/pretrained_models/easycv/genet/GENet_normal.pth', '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) out: (M, D)
""" """
assert embed_dim % 2 == 0 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 /= embed_dim / 2.
omega = 1. / 10000**omega # (D/2,) omega = 1. / 10000**omega # (D/2,)

View File

@ -1,5 +1,7 @@
# Copyright (c) Alibaba, Inc. and its affiliates. # Copyright (c) Alibaba, Inc. and its affiliates.
import glob
import math import math
import os
import numpy as np import numpy as np
import torch import torch
@ -7,11 +9,18 @@ from PIL import Image
from easycv.file import io from easycv.file import io
from easycv.framework.errors import ValueError from easycv.framework.errors import ValueError
from easycv.utils.checkpoint import load_checkpoint
from easycv.utils.misc import deprecated from easycv.utils.misc import deprecated
from .base import InputProcessor, OutputProcessor, Predictor, PredictorV2 from .base import InputProcessor, OutputProcessor, Predictor, PredictorV2
from .builder import PREDICTORS 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): class ClsInputProcessor(InputProcessor):
"""Process inputs for classification models. """Process inputs for classification models.
@ -146,6 +155,20 @@ class ClassificationPredictor(PredictorV2):
self.pil_input = pil_input self.pil_input = pil_input
self.label_map_path = label_map_path 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: if self.pil_input:
mode = 'RGB' mode = 'RGB'
super(ClassificationPredictor, self).__init__( super(ClassificationPredictor, self).__init__(
@ -186,6 +209,41 @@ class ClassificationPredictor(PredictorV2):
return ClsOutputProcessor(topk=self.topk, label_map=self.label_map) 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: try:
from easy_vision.python.inference.predictor import PredictorInterface from easy_vision.python.inference.predictor import PredictorInterface

View File

@ -23,6 +23,12 @@ except Exception:
from .interface import PredictorInterface 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): class DetInputProcessor(InputProcessor):
def build_processor(self): def build_processor(self):
@ -349,9 +355,11 @@ class YoloXPredictor(DetectionPredictor):
self.model_type = 'jit' self.model_type = 'jit'
elif model_path.endswith('blade'): elif model_path.endswith('blade'):
self.model_type = 'blade' self.model_type = 'blade'
elif model_path.endswith('onnx'):
self.model_type = 'onnx'
else: else:
self.model_type = 'raw' 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: if self.model_type == 'blade' or self.use_trt_efficientnms:
import torch_blade import torch_blade
@ -381,8 +389,16 @@ class YoloXPredictor(DetectionPredictor):
def _build_model(self): def _build_model(self):
if self.model_type != 'raw': if self.model_type != 'raw':
with io.open(self.model_path, 'rb') as infile: if self.model_type != 'onnx':
model = torch.jit.load(infile, self.device) 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: else:
from easycv.utils.misc import reparameterize_models from easycv.utils.misc import reparameterize_models
model = super()._build_model() model = super()._build_model()
@ -394,8 +410,9 @@ class YoloXPredictor(DetectionPredictor):
If the model is not loaded from a configuration file, e.g. torch jit model, you need to reimplement it. If the model is not loaded from a configuration file, e.g. torch jit model, you need to reimplement it.
""" """
model = self._build_model() model = self._build_model()
model.to(self.device) if self.model_type != 'onnx':
model.eval() model.to(self.device)
model.eval()
if self.model_type == 'raw': if self.model_type == 'raw':
load_checkpoint(model, self.model_path, map_location='cpu') load_checkpoint(model, self.model_path, map_location='cpu')
return model return model
@ -406,7 +423,15 @@ class YoloXPredictor(DetectionPredictor):
""" """
if self.model_type != 'raw': if self.model_type != 'raw':
with torch.no_grad(): with torch.no_grad():
outputs = self.model(inputs['img']) 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 outputs = {'results': outputs} # convert to dict format
else: else:
outputs = super().model_forward(inputs) 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 easycv.utils.misc import deprecated
from .base import InputProcessor, OutputProcessor, PredictorV2 from .base import InputProcessor, OutputProcessor, PredictorV2
np.set_printoptions(suppress=True)
def _box2cs(image_size, box): def _box2cs(image_size, box):
"""This encodes bbox(x,y,w,h) into (center, scale) """This encodes bbox(x,y,w,h) into (center, scale)
@ -222,11 +224,12 @@ class PoseTopDownInputProcessor(InputProcessor):
bboxes = bboxes[valid_idx] bboxes = bboxes[valid_idx]
person_results = [person_results[i] for i in valid_idx] person_results = [person_results[i] for i in valid_idx]
output_person_info = [] results = []
for person_result in person_results: for person_result in person_results:
box = person_result['bbox'] # x,y,x,y box = person_result['bbox'] # x,y,x,y,s
box = [box[0], box[1], box[2] - box[0], box[3] - box[1]] # x,y,w,h boxc = [box[0], box[1], box[2] - box[0],
center, scale = _box2cs(self.cfg.data_cfg['image_size'], box) box[3] - box[1]] # x,y,w,h
center, scale = _box2cs(self.cfg.data_cfg['image_size'], boxc)
data = { data = {
'image_id': 'image_id':
0, 0,
@ -264,11 +267,10 @@ class PoseTopDownInputProcessor(InputProcessor):
output['img_fields'], output['img_fields'],
} }
box_id += 1 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 return results
def __call__(self, inputs): def __call__(self, inputs):
@ -296,12 +298,7 @@ class PoseTopDownOutputProcessor(OutputProcessor):
def __call__(self, inputs): def __call__(self, inputs):
output = {} output = {}
output['keypoints'] = inputs['preds'] output['keypoints'] = inputs['preds']
output['bbox'] = inputs['boxes'] # c1, c2, s1, s2, area, core output['bbox'] = np.array(inputs['boxes']) # x1, y1, x2, y2 score
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]]
return output return output
@ -403,6 +400,7 @@ class PoseTopDownPredictor(PredictorV2):
return model return model
def model_forward(self, inputs, return_heatmap=False): def model_forward(self, inputs, return_heatmap=False):
boxes = inputs['bbox'].cpu().numpy()
if self.model_type == 'raw': if self.model_type == 'raw':
with torch.no_grad(): with torch.no_grad():
result = self.model( result = self.model(
@ -423,6 +421,7 @@ class PoseTopDownPredictor(PredictorV2):
result = decode_heatmap(output_heatmap, img_metas, result = decode_heatmap(output_heatmap, img_metas,
self.cfg.model.test_cfg) self.cfg.model.test_cfg)
result['boxes'] = np.array(boxes)
return result return result
def get_input_processor(self): def get_input_processor(self):

View File

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

View File

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

View File

@ -48,9 +48,15 @@ class EasyCVDetectionPipeline(EasyCVPipeline):
labels = [] labels = []
boxes = [] boxes = []
for output in outputs: for output in outputs:
for score, label, box in zip(output['detection_scores'], scores_list = output['detection_scores'] if output[
output['detection_classes'], 'detection_scores'] is not None else []
output['detection_boxes']): 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) scores.append(score)
labels.append(self.cfg.CLASSES[label]) labels.append(self.cfg.CLASSES[label])
boxes.append([b for b in box]) 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', 'configs/classification/imagenet/swint/imagenet_swin_tiny_patch4_window7_224_jpg.py',
'CLASSIFICATION_M0BILENET': 'CLASSIFICATION_M0BILENET':
'configs/classification/imagenet/mobilenet/mobilenetv2.py', '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 # metric learning
'METRICLEARNING': 'METRICLEARNING':

View File

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

View File

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

View File

@ -116,6 +116,7 @@ class ModelExportTest(unittest.TestCase):
cfg = mmcv_config_fromfile(config_file) cfg = mmcv_config_fromfile(config_file)
cfg_options = { cfg_options = {
'model.backbone.norm_cfg.type': 'SyncBN', 'model.backbone.norm_cfg.type': 'SyncBN',
'export.export_type': 'raw'
} }
if cfg_options is not None: if cfg_options is not None:
cfg.merge_from_dict(cfg_options) cfg.merge_from_dict(cfg_options)
@ -210,6 +211,27 @@ class ModelExportTest(unittest.TestCase):
self.assertTrue(os.path.exists(filename + '.jit')) 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__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -89,8 +89,8 @@ class CocoToolsTest(unittest.TestCase):
def testExportGroundtruthToCOCO(self): def testExportGroundtruthToCOCO(self):
image_ids = ['first', 'second'] image_ids = ['first', 'second']
groundtruth_boxes = [ groundtruth_boxes = [
np.array([[100, 100, 200, 200]], np.float), np.array([[100, 100, 200, 200]], np.float32),
np.array([[50, 50, 100, 100]], np.float) np.array([[50, 50, 100, 100]], np.float32)
] ]
groundtruth_classes = [ groundtruth_classes = [
np.array([1], np.int32), np.array([1], np.int32),
@ -126,12 +126,12 @@ class CocoToolsTest(unittest.TestCase):
def testExportDetectionsToCOCO(self): def testExportDetectionsToCOCO(self):
image_ids = ['first', 'second'] image_ids = ['first', 'second']
detections_boxes = [ detections_boxes = [
np.array([[100, 100, 200, 200]], np.float), np.array([[100, 100, 200, 200]], np.float32),
np.array([[50, 50, 100, 100]], np.float) np.array([[50, 50, 100, 100]], np.float32)
] ]
detections_scores = [ detections_scores = [
np.array([.8], np.float), np.array([.8], np.float32),
np.array([.7], np.float) np.array([.7], np.float32)
] ]
detections_classes = [np.array([1], np.int32), np.array([1], np.int32)] detections_classes = [np.array([1], np.int32), np.array([1], np.int32)]
categories = [{ categories = [{
@ -152,7 +152,17 @@ class CocoToolsTest(unittest.TestCase):
detections_classes, detections_classes,
categories, categories,
output_path=output_path) 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: with io.open(output_path, 'r') as f:
written_result = f.read() written_result = f.read()
# The json output should have floats written to 4 digits of precision. # The json output should have floats written to 4 digits of precision.
@ -160,7 +170,10 @@ class CocoToolsTest(unittest.TestCase):
re.MULTILINE) re.MULTILINE)
self.assertTrue(matcher.findall(written_result)) self.assertTrue(matcher.findall(written_result))
written_result = json.loads(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): def testExportSegmentsToCOCO(self):
image_ids = ['first', 'second'] image_ids = ['first', 'second']
@ -176,7 +189,10 @@ class CocoToolsTest(unittest.TestCase):
for i, detection_mask in enumerate(detection_masks): for i, detection_mask in enumerate(detection_masks):
detection_masks[i] = detection_mask[:, :, :, None] 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)] detection_classes = [np.array([1], np.int32), np.array([1], np.int32)]
categories = [{ categories = [{
@ -202,7 +218,12 @@ class CocoToolsTest(unittest.TestCase):
written_result = json.loads(written_result) written_result = json.loads(written_result)
mask_load = mask.decode([written_result[0]['segmentation']]) mask_load = mask.decode([written_result[0]['segmentation']])
self.assertTrue(np.allclose(mask_load, detection_masks[0])) 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): def testExportKeypointsToCOCO(self):
image_ids = ['first', 'second'] image_ids = ['first', 'second']
@ -216,8 +237,8 @@ class CocoToolsTest(unittest.TestCase):
] ]
detection_scores = [ detection_scores = [
np.array([.8, 0.2], np.float), np.array([.8, 0.2], np.float32),
np.array([.7, 0.3], np.float) np.array([.7, 0.3], np.float32)
] ]
detection_classes = [ detection_classes = [
np.array([1, 1], np.int32), np.array([1, 1], np.int32),
@ -248,7 +269,12 @@ class CocoToolsTest(unittest.TestCase):
with io.open(output_path, 'r') as f: with io.open(output_path, 'r') as f:
written_result = f.read() written_result = f.read()
written_result = json.loads(written_result) 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): def testSingleImageDetectionBoxesExport(self):
boxes = np.array([[0, 0, 1, 1], [0, 0, .5, .5], [.5, .5, 1, 1]], 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) output = model(imgs, mode='train', **kwargs)
self.assertEqual(output['img_h'].cpu().numpy(), 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(), 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['total_loss'].shape, torch.Size([]))
self.assertEqual(output['iou_l'].shape, torch.Size([])) self.assertEqual(output['iou_l'].shape, torch.Size([]))
self.assertEqual(output['conf_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) output = model(imgs, mode='train', **kwargs)
self.assertEqual(output['img_h'].cpu().numpy(), 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(), 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['total_loss'].shape, torch.Size([]))
self.assertEqual(output['iou_l'].shape, torch.Size([])) self.assertEqual(output['iou_l'].shape, torch.Size([]))
self.assertEqual(output['conf_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.predictors.classifier import ClassificationPredictor
from easycv.utils.test_util import clean_up, get_tmp_dir from easycv.utils.test_util import clean_up, get_tmp_dir
from tests.ut_config import (PRETRAINED_MODEL_RESNET50_WITHOUTHEAD, 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): class ClassificationPredictorTest(unittest.TestCase):
@ -33,6 +34,17 @@ class ClassificationPredictorTest(unittest.TestCase):
self.assertListEqual(results['class_name'], ['"Persian cat",']) self.assertListEqual(results['class_name'], ['"Persian cat",'])
self.assertEqual(len(results['class_probs']), 1000) 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): def test_batch(self):
checkpoint = PRETRAINED_MODEL_RESNET50_WITHOUTHEAD checkpoint = PRETRAINED_MODEL_RESNET50_WITHOUTHEAD
config_file = 'configs/classification/imagenet/resnet/imagenet_resnet50_jpg.py' config_file = 'configs/classification/imagenet/resnet/imagenet_resnet50_jpg.py'

View File

@ -54,10 +54,10 @@ class PoseTopDownPredictorTest(unittest.TestCase):
assert_array_almost_equal( assert_array_almost_equal(
result0['bbox'], result0['bbox'],
np.array([[352.3085, 59.00325, 691.4247, 511.15814, 1.], np.array([[438.9, 59., 604.8, 511.2, 0.9],
[10.511196, 177.74883, 101.824326, 299.49966, 1.], [10.5, 179.6, 101.8, 297.7, 0.9],
[224.82036, 114.439865, 312.51306, 231.36348, 1.], [229.6, 114.4, 307.8, 231.4, 0.6],
[200.71407, 114.716736, 337.17535, 296.6651, 1.]], [229.4, 114.7, 308.5, 296.7, 0.6]],
dtype=np.float32), dtype=np.float32),
decimal=1) decimal=1)
vis_result = predictor.show_result(img1, result0) vis_result = predictor.show_result(img1, result0)
@ -92,10 +92,10 @@ class PoseTopDownPredictorTest(unittest.TestCase):
assert_array_almost_equal( assert_array_almost_equal(
result1['bbox'][:4], result1['bbox'][:4],
np.array([[436.23096, 214.72766, 584.26013, 412.09985, 1.], np.array([[470.6, 214.7, 549.9, 412.1, 0.9],
[43.990044, 91.04126, 164.28406, 251.43329, 1.], [71.6, 91., 136.7, 251.4, 0.9],
[127.44148, 100.38604, 254.219, 269.42273, 1.], [159.7, 100.4, 221.9, 269.4, 0.9],
[190.08075, 117.31801, 311.22394, 278.8423, 1.]], [219.4, 117.3, 281.9, 278.8, 0.9]],
dtype=np.float32), dtype=np.float32),
decimal=1) decimal=1)
vis_result = predictor.show_result(img2, result1) 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.pipelines import pipeline
from modelscope.utils.constant import Tasks from modelscope.utils.constant import Tasks
from modelscope.utils.cv.image_utils import panoptic_seg_masks_to_image 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 modelscope.utils.test_utils import test_level
from tests.ut_config import BASE_LOCAL_PATH from tests.ut_config import BASE_LOCAL_PATH
class EasyCVPanopticSegmentationPipelineTest(unittest.TestCase, class EasyCVPanopticSegmentationPipelineTest(unittest.TestCase):
DemoCompatibilityCheck):
img_path = os.path.join( img_path = os.path.join(
BASE_LOCAL_PATH, 'data/test_images/image_semantic_segmentation.jpg') 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) cv2.imwrite(tmp_save_path, draw_img)
print('print ' + self.model_id + ' success') 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__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -9,14 +9,12 @@ from modelscope.outputs import OutputKeys
from modelscope.pipelines import pipeline from modelscope.pipelines import pipeline
from modelscope.utils.constant import Tasks from modelscope.utils.constant import Tasks
from modelscope.utils.cv.image_utils import semantic_seg_masks_to_image 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 modelscope.utils.test_utils import test_level
from PIL import Image from PIL import Image
from tests.ut_config import BASE_LOCAL_PATH from tests.ut_config import BASE_LOCAL_PATH
class EasyCVSegmentationPipelineTest(unittest.TestCase, class EasyCVSegmentationPipelineTest(unittest.TestCase):
DemoCompatibilityCheck):
img_path = os.path.join(BASE_LOCAL_PATH, img_path = os.path.join(BASE_LOCAL_PATH,
'data/test_images/image_segmentation.jpg') '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' model_id = 'damo/cv_segformer-b5_image_semantic-segmentation_coco-stuff164k'
self._internal_test_(model_id) 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__': if __name__ == '__main__':
unittest.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() oss_config = get_oss_config()
ak_id = oss_config['ak_id'] ak_id = oss_config['ak_id']
ak_secret = oss_config['ak_secret'] ak_secret = oss_config['ak_secret']
hosts = oss_config['hosts'] + ['oss-cn-hangzhou.aliyuncs.com'] hosts = oss_config['hosts']
hosts = ','.join(_ for _ in hosts) hosts = ','.join(_ for _ in hosts)
buckets = oss_config['buckets'] + ['easycv'] buckets = oss_config['buckets']
buckets = ','.join(_ for _ in 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 output_file = tempfile.NamedTemporaryFile('w').name
cmd = f'PYTHONPATH=. python tools/predict.py \ cmd = f'PYTHONPATH=. python tools/predict.py \
--input_file {input_file} \ --input_file {input_file} \

View File

@ -179,6 +179,9 @@ PRETRAINED_MODEL_RESNET50 = os.path.join(
PRETRAINED_MODEL_RESNET50_WITHOUTHEAD = os.path.join( PRETRAINED_MODEL_RESNET50_WITHOUTHEAD = os.path.join(
BASE_LOCAL_PATH, BASE_LOCAL_PATH,
'pretrained_models/classification/resnet/resnet50_withhead.pth') '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_MODEL_FACEID = os.path.join(BASE_LOCAL_PATH,
'pretrained_models/faceid') 'pretrained_models/faceid')
PRETRAINED_MODEL_YOLOXS_EXPORT = os.path.join( PRETRAINED_MODEL_YOLOXS_EXPORT = os.path.join(