logo
Browse Source

add uniformer

Signed-off-by: gexy5 <xinyu.ge@zilliz.com>
main
gexy5 3 years ago
parent
commit
d55ede163b
  1. 106
      README.md
  2. 19
      __init__.py
  3. 1
      kinetics_400.json
  4. 131
      uniformer.py
  5. BIN
      uniformer_base_k400_16x4.pth
  6. BIN
      uniformer_base_k400_8x8.pth
  7. BIN
      uniformer_small_k400_16x4.pth
  8. BIN
      uniformer_small_k400_8x8.pth

106
README.md

@ -1,2 +1,106 @@
# uniformer
# Video Classification with Uniformer
*Author: [Xinyu Ge](https://github.com/gexy185)*
<br />
## Description
A video classification operator generates labels (and corresponding scores) and extracts features for the input video.
It transforms the video into frames and loads pre-trained models by model names.
This operator has implemented pre-trained models from [Uniformer](https://arxiv.org/abs/2201.09450)
and maps vectors with labels provided by datasets used for pre-training.
<br />
## Code Example
Use the pretrained Uniformer model to classify and generate a vector for the given video path './archery.mp4'
([download](https://dl.fbaipublicfiles.com/pytorchvideo/projects/archery.mp4)).
*Write the pipeline in simplified style*:
- Predict labels (default):
```python
import towhee
(
towhee.glob('./archery.mp4')
.video_decode.ffmpeg()
.action_classification.uniformer(
model_name='omnivore_swinT', topk=5)
.show()
)
```
<img src="./result1.png" height="px"/>
*Write a same pipeline with explicit inputs/outputs name specifications*:
```python
import towhee
(
towhee.glob['path']('./archery.mp4')
.video_decode.ffmpeg['path', 'frames']()
.action_classification.uniformer['frames', ('labels', 'scores', 'features')](
model_name='uniformer_k400_s8')
.select['path', 'labels', 'scores', 'features']()
.show(formatter={'path': 'video_path'})
)
```
<img src="./result2.png" height="px"/>
<br />
## Factory Constructor
Create the operator via the following factory method
***video_classification.uniformer(
model_name='omnivore_swinT', skip_preprocess=False, classmap=None, topk=5)***
**Parameters:**
***model_name***: *str*
​ The name of pre-trained uniformer model.
​ Supported model names:
- uniformer_k400_s8
- uniformer_k400_s16
- uniformer_k400_b8
- uniformer_k400_b16
***skip_preprocess***: *bool*
​ Flag to control whether to skip video transforms, defaults to False.
If set to True, the step to transform videos will be skipped.
In this case, the user should guarantee that all the input video frames are already reprocessed properly,
and thus can be fed to model directly.
***classmap***: *Dict[str: int]*:
​ Dictionary that maps class names to one hot vectors.
If not given, the operator will load the default class map dictionary.
***topk***: *int*
​ The topk labels & scores to present in result. The default value is 5.
## Interface
A video classification operator generates a list of class labels
and a corresponding vector in numpy.ndarray given a video input data.
**Parameters:**
***video***: *Union[str, numpy.ndarray]*
​ Input video data using local path in string or video frames in ndarray.
**Returns**: *(list, list, torch.Tensor)*
​ A tuple of (labels, scores, features),
which contains lists of predicted class names and corresponding scores.

19
__init__.py

@ -0,0 +1,19 @@
# Copyright 2022 Zilliz. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from .uniformer import Uniformer
def uniformer(**kwargs):
return Uniformer(**kwargs)

1
kinetics_400.json

File diff suppressed because one or more lines are too long

131
uniformer.py

@ -0,0 +1,131 @@
import logging
import os
import json
from pathlib import Path
from typing import List
import torch
import numpy
from towhee import register
from towhee.operator.base import NNOperator
from towhee.types.video_frame import VideoFrame
from towhee.models.utils.video_transforms import get_configs, transform_video
from towhee.models.uniformer.uniformer import create_model
log = logging.getLogger()
@register(output_schema=['labels', 'scores', 'features'])
class Tsm(NNOperator):
"""
Generate a list of class labels given a video input data.
Default labels are from [Kinetics400 Dataset](https://deepmind.com/research/open-source/kinetics).
Args:
model_name (`str`):
Supported model names:
- uniformer_k400_s8
- uniformer_k400_s16
- uniformer_k400_b8
- uniformer_k400_b16
skip_preprocess (`str`):
Flag to skip video transforms.
predict (`bool`):
Flag to control whether predict labels. If False, then return video embedding.
classmap (`dict=None`):
The dictionary maps classes to integers.
topk (`int=5`):
The number of classification labels to be returned (ordered by possibility from high to low).
"""
def __init__(self,
model_name: str = 'uniformer_k400_s8',
framework: str = 'pytorch',
skip_preprocess: bool = False,
classmap: dict = None,
topk: int = 5,
):
super().__init__(framework=framework)
self.model_name = model_name
self.skip_preprocess = skip_preprocess
self.topk = topk
if 'k400' in model_name:
self.dataset_name = 'kinetics_400'
if classmap is None:
class_file = os.path.join(str(Path(__file__).parent), self.dataset_name+'.json')
with open(class_file, "r") as f:
kinetics_classes = json.load(f)
self.classmap = {}
for k, v in kinetics_classes.items():
self.classmap[v] = str(k).replace('"', '')
else:
self.classmap = classmap
self.device = 'cuda' if torch.cuda.is_available() else 'cpu'
self.input_mean = [0.45, 0.45, 0.45]
self.input_std = [0.225, 0.225, 0.225]
model_path = str(Path(__file__).parent)
model_path = {
'uniformer_k400_s8': os.path.join(model_path, 'uniformer_small_k400_8x8.pth'),
'uniformer_k400_s16': os.path.join(model_path, 'uniformer_small_k400_16x4.pth'),
'uniformer_k400_b8': os.path.join(model_path, 'uniformer_base_k400_8x8.pth'),
'uniformer_k400_b16': os.path.join(model_path, 'uniformer_base_k400_16x4.pth'),
}
self.weights_path = model_path[model_name]
self.model = create_model(model_name=model_name, pretrained=True, weights_path=self.weights_path, device=self.device)
if '8' in model_name:
self.transform_cfgs = get_configs(
side_size=256,
crop_size=224,
num_frames=8,
mean=self.input_mean,
std=self.input_std,
)
elif '16' in model_name:
self.transform_cfgs = get_configs(
side_size=256,
crop_size=224,
num_frames=16,
mean=self.input_mean,
std=self.input_std,
)
self.model.eval()
def __call__(self, video: List[VideoFrame]):
"""
Args:
video (`List[VideoFrame]`):
Video path in string.
Returns:
(labels, scores)
A tuple of lists (labels, scores).
OR emb
Video embedding.
"""
# Convert list of towhee.types.Image to numpy.ndarray in float32
video = numpy.stack([img.astype(numpy.float32)/255. for img in video], axis=0)
assert len(video.shape) == 4
video = video.transpose(3, 0, 1, 2) # twhc -> ctwh
# Transform video data given configs
if self.skip_preprocess:
self.transform_cfgs.update(num_frames=None)
data = transform_video(
video=video,
**self.transform_cfgs
)
inputs = data.to(self.device)[None, ...]
feats = self.model.forward_features(inputs)
features = feats.to('cpu').squeeze(0).detach().numpy()
feats = feats.flatten(2).mean(-1)
outs = self.model.head(feats)
post_act = torch.nn.Softmax(dim=1)
preds = post_act(outs)
pred_scores, pred_classes = preds.topk(k=self.topk)
labels = [self.classmap[int(i)] for i in pred_classes[0]]
scores = [round(float(x), 5) for x in pred_scores[0]]
return labels, scores, features

BIN
uniformer_base_k400_16x4.pth (Stored with Git LFS)

Binary file not shown.

BIN
uniformer_base_k400_8x8.pth (Stored with Git LFS)

Binary file not shown.

BIN
uniformer_small_k400_16x4.pth (Stored with Git LFS)

Binary file not shown.

BIN
uniformer_small_k400_8x8.pth (Stored with Git LFS)

Binary file not shown.
Loading…
Cancel
Save