├── .github ├── dependabot.yml └── workflows │ ├── docs-build.yml │ ├── stale.yml │ └── sync_issues.yml ├── .gitignore ├── MANIFEST.in ├── README.md ├── README_zh-CN.md ├── configs ├── __init__.py ├── _base_ │ ├── default_runtime.py │ └── schedules │ │ ├── AdamW_linear_coslr_bs2048.py │ │ ├── schedule_1x.py │ │ └── sgd_linear_coslr_bs2048.py ├── anomaly │ └── vae_mirophone.py ├── cls │ └── timm_classify.py ├── datasets │ ├── coco_detection.py │ ├── imagenet_bs32.py │ └── lancedb_bs32.py ├── fomo │ ├── fomo_mobnetv2_0.1_x8_coco.py │ ├── fomo_mobnetv2_0.35_x8_coco.py │ └── fomo_mobnetv2_1_x16_coco.py ├── pfld │ ├── pfld_mbv2_1000e.py │ └── pfld_mbv3l_192_1000e.py ├── pretrain │ ├── default_runtime.py │ ├── imagenet_bs2048_rsb.py │ ├── mobilenetv4_imagenet.py │ └── resnet50_imagenet.py └── rtmdet │ ├── rtmdet_nano_8xb32_300e_coco_ncadc_relu6.py │ ├── rtmdet_nano_8xb32_300e_coco_relu.py │ └── rtmdet_nano_8xb32_300e_coco_relu_q.py ├── docs ├── .vitepress │ ├── config.ts │ ├── locales │ │ ├── en_US.ts │ │ └── zh_CN.ts │ └── theme │ │ ├── index.ts │ │ └── style.css ├── community │ ├── contributing.md │ ├── faqs.md │ ├── license.md │ └── reference.md ├── custom │ ├── basics.md │ ├── model.md │ ├── optimizer.md │ └── pipelines.md ├── datasets │ ├── custom.md │ ├── extension.md │ ├── images │ │ ├── labelme_pfld_1p.png │ │ ├── roboflow_download.png │ │ ├── roboflow_fomo.png │ │ └── roboflow_generate_version.png │ └── public.md ├── images │ └── rtmdet_coco_eval.png ├── index.md ├── introduction │ ├── installation.md │ ├── overview.md │ └── quick_start.md ├── tutorials │ ├── deploy │ │ ├── grove_vision_ai_v2.md │ │ ├── images │ │ │ └── sscma-upload.png │ │ ├── overview.md │ │ └── xiao_esp32s3.md │ ├── overview.md │ └── training │ │ ├── fomo.md │ │ ├── overview.md │ │ ├── pfld.md │ │ ├── rtmdet.md │ │ └── vae.md └── zh_cn │ ├── community │ ├── contributing.md │ ├── faqs.md │ ├── license.md │ └── reference.md │ ├── custom │ ├── basics.md │ ├── model.md │ ├── optimizer.md │ └── pipelines.md │ ├── datasets │ ├── custom.md │ ├── extension.md │ ├── images │ │ ├── labelme_pfld_1p.png │ │ ├── roboflow_download.png │ │ ├── roboflow_fomo.png │ │ └── roboflow_generate_version.png │ └── public.md │ ├── index.md │ ├── introduction │ ├── installation.md │ ├── overview.md │ └── quick_start.md │ └── tutorials │ ├── deploy │ ├── grove_vision_ai_v2.md │ ├── images │ │ └── sscma-upload.png │ ├── overview.md │ └── xiao_esp32s3.md │ ├── overview.md │ └── training │ ├── fomo.md │ ├── overview.md │ ├── pfld.md │ ├── rtmdet.md │ └── vae.md ├── mmengine ├── __init__.py ├── _strategy │ ├── __init__.py │ ├── base.py │ ├── colossalai.py │ ├── deepspeed.py │ ├── distributed.py │ ├── fsdp.py │ ├── single_device.py │ └── utils.py ├── analysis │ ├── __init__.py │ ├── complexity_analysis.py │ ├── jit_analysis.py │ ├── jit_handles.py │ └── print_helper.py ├── config │ ├── __init__.py │ ├── config.py │ ├── lazy.py │ └── utils.py ├── dataset │ ├── __init__.py │ ├── base_dataset.py │ ├── dataset_wrapper.py │ ├── sampler.py │ └── utils.py ├── device │ ├── __init__.py │ └── utils.py ├── dist │ ├── __init__.py │ ├── dist.py │ └── utils.py ├── evaluator │ ├── __init__.py │ ├── evaluator.py │ ├── metric.py │ └── utils.py ├── fileio │ ├── __init__.py │ ├── backends │ │ ├── __init__.py │ │ ├── base.py │ │ ├── http_backend.py │ │ ├── lmdb_backend.py │ │ ├── local_backend.py │ │ ├── memcached_backend.py │ │ ├── petrel_backend.py │ │ └── registry_utils.py │ ├── file_client.py │ ├── handlers │ │ ├── __init__.py │ │ ├── base.py │ │ ├── json_handler.py │ │ ├── pickle_handler.py │ │ ├── registry_utils.py │ │ └── yaml_handler.py │ ├── io.py │ └── parse.py ├── hooks │ ├── __init__.py │ ├── checkpoint_hook.py │ ├── early_stopping_hook.py │ ├── ema_hook.py │ ├── empty_cache_hook.py │ ├── hook.py │ ├── iter_timer_hook.py │ ├── logger_hook.py │ ├── naive_visualization_hook.py │ ├── param_scheduler_hook.py │ ├── profiler_hook.py │ ├── runtime_info_hook.py │ ├── sampler_seed_hook.py │ ├── sync_buffer_hook.py │ ├── test_time_aug_hook.py │ └── visualization_hook.py ├── hub │ ├── __init__.py │ ├── deprecated.json │ ├── hub.py │ ├── mmcls.json │ ├── openmmlab.json │ └── torchvision_0.12.json ├── infer │ ├── __init__.py │ └── infer.py ├── logging │ ├── __init__.py │ ├── history_buffer.py │ ├── logger.py │ └── message_hub.py ├── model │ ├── __init__.py │ ├── averaged_model.py │ ├── base_model │ │ ├── __init__.py │ │ ├── base_model.py │ │ └── data_preprocessor.py │ ├── base_module.py │ ├── efficient_conv_bn_eval.py │ ├── plugin.py │ ├── test_time_aug.py │ ├── utils.py │ ├── weight_init.py │ └── wrappers │ │ ├── __init__.py │ │ ├── distributed.py │ │ ├── fully_sharded_distributed.py │ │ ├── seperate_distributed.py │ │ └── utils.py ├── optim │ ├── __init__.py │ ├── optimizer │ │ ├── __init__.py │ │ ├── amp_optimizer_wrapper.py │ │ ├── apex_optimizer_wrapper.py │ │ ├── base.py │ │ ├── builder.py │ │ ├── default_constructor.py │ │ ├── optimizer_wrapper.py │ │ ├── optimizer_wrapper_dict.py │ │ └── zero_optimizer.py │ └── scheduler │ │ ├── __init__.py │ │ ├── lr_scheduler.py │ │ ├── momentum_scheduler.py │ │ └── param_scheduler.py ├── registry │ ├── __init__.py │ ├── build_functions.py │ ├── default_scope.py │ ├── registry.py │ ├── root.py │ └── utils.py ├── runner │ ├── __init__.py │ ├── _flexible_runner.py │ ├── activation_checkpointing.py │ ├── amp.py │ ├── base_loop.py │ ├── checkpoint.py │ ├── log_processor.py │ ├── loops.py │ ├── priority.py │ ├── runner.py │ └── utils.py ├── structures │ ├── __init__.py │ ├── base_data_element.py │ ├── instance_data.py │ ├── label_data.py │ └── pixel_data.py ├── testing │ ├── __init__.py │ ├── _internal │ │ ├── __init__.py │ │ └── distributed.py │ ├── compare.py │ └── runner_test_case.py ├── utils │ ├── __init__.py │ ├── dl_utils │ │ ├── __init__.py │ │ ├── collect_env.py │ │ ├── hub.py │ │ ├── misc.py │ │ ├── parrots_wrapper.py │ │ ├── setup_env.py │ │ ├── time_counter.py │ │ ├── torch_ops.py │ │ ├── trace.py │ │ └── visualize.py │ ├── manager.py │ ├── misc.py │ ├── package_utils.py │ ├── path.py │ ├── progressbar.py │ ├── progressbar_rich.py │ ├── timer.py │ └── version_utils.py ├── version.py └── visualization │ ├── __init__.py │ ├── utils.py │ ├── vis_backend.py │ └── visualizer.py ├── package-lock.json ├── package.json ├── requirements.txt ├── setup.cfg ├── setup.py ├── sscma ├── __init__.py ├── datasets │ ├── __init__.py │ ├── anomaly_dataset.py │ ├── api_wrappers │ │ ├── __init__.py │ │ ├── coco_api.py │ │ └── cocoeval_mp.py │ ├── base_dataset.py │ ├── base_det_dataset.py │ ├── categories.py │ ├── coco.py │ ├── custom.py │ ├── data_preprocessor.py │ ├── fomo.py │ ├── imagenet.py │ ├── lancedb_datasets.py │ ├── meter.py │ ├── pipelines │ │ ├── __init__.py │ │ └── composition.py │ ├── samplers │ │ ├── __init__.py │ │ └── batch_sampler.py │ ├── transforms │ │ ├── __init__.py │ │ ├── basetransform.py │ │ ├── formatting.py │ │ ├── loading.py │ │ ├── processing.py │ │ ├── transforms.py │ │ ├── utils.py │ │ └── wrappers.py │ └── utils │ │ ├── __init__.py │ │ ├── download.py │ │ ├── parse_pose.py │ │ └── sampler.py ├── deploy │ ├── backend │ │ ├── __init__.py │ │ ├── base_infer.py │ │ ├── hailo_infer.py │ │ ├── onnxruntime_infer.py │ │ ├── saved_model_infer.py │ │ ├── tflite_infer.py │ │ └── torchscript_infer.py │ ├── models │ │ ├── __init__.py │ │ ├── anomaly_infer.py │ │ ├── fomo_infer.py │ │ ├── pfld_infer.py │ │ └── rtmdet_infer.py │ └── utils.py ├── engine │ ├── __init__.py │ ├── hooks │ │ ├── __init__.py │ │ ├── memory_profiler_hook.py │ │ ├── pipeline_switch_hook.py │ │ ├── quantizer_switch_hook.py │ │ └── visualization_hook.py │ └── schedulers │ │ ├── __init__.py │ │ └── quadratic_warmup.py ├── evaluation │ ├── __init__.py │ ├── base_metric.py │ ├── bbox_overlaps.py │ ├── coco_metric.py │ ├── dist.py │ ├── dist_backends.py │ ├── evaluator.py │ ├── fomo_metric.py │ ├── metrics.py │ ├── mse_metric.py │ ├── panoptic_utils.py │ ├── point_metric.py │ └── recall.py ├── infer │ ├── __init__.py │ └── inference.py ├── models │ ├── __init__.py │ ├── backbones │ │ ├── __init__.py │ │ ├── backbone.py │ │ ├── base_backbone.py │ │ ├── csp_darknet.py │ │ ├── cspnext.py │ │ ├── mobilenetv2.py │ │ ├── mobilenetv3.py │ │ └── timm.py │ ├── base │ │ ├── __init__.py │ │ └── general.py │ ├── classifiers │ │ ├── __init__.py │ │ └── image.py │ ├── cnn │ │ ├── __init__.py │ │ ├── activation.py │ │ ├── conv.py │ │ ├── conv_block.py │ │ ├── conv_module.py │ │ ├── depthwise_separable_conv_module.py │ │ ├── drop.py │ │ ├── norm.py │ │ ├── padding.py │ │ └── scale.py │ ├── detectors │ │ ├── __init__.py │ │ ├── anomaly.py │ │ ├── base.py │ │ ├── fomo.py │ │ ├── pfld.py │ │ ├── rtmdet.py │ │ └── single_stage.py │ ├── heads │ │ ├── __init__.py │ │ ├── anchor_head.py │ │ ├── atss_head.py │ │ ├── base_dense_head.py │ │ ├── cls_head.py │ │ ├── fomo_head.py │ │ ├── pfld_head.py │ │ └── rtmdet_head.py │ ├── layers │ │ ├── __init__.py │ │ ├── csp_layer.py │ │ ├── decode.py │ │ ├── ema.py │ │ ├── encode.py │ │ ├── rep.py │ │ ├── se_layer.py │ │ └── utils.py │ ├── losses │ │ ├── __init__.py │ │ ├── cross_entropy_loss.py │ │ ├── gfocal_loss.py │ │ ├── iou_loss.py │ │ ├── label_smooth_loss.py │ │ ├── pfld_loss.py │ │ └── utils.py │ ├── necks │ │ ├── __init__.py │ │ ├── cspnext_pafpn.py │ │ ├── fpn.py │ │ ├── gap.py │ │ ├── pafpn.py │ │ └── sppf.py │ ├── task_modules │ │ ├── __init__.py │ │ ├── assigners │ │ │ ├── __init__.py │ │ │ ├── assign_result.py │ │ │ ├── base_assigner.py │ │ │ ├── batch_dsl_assigner.py │ │ │ ├── dynamic_soft_label_assigner.py │ │ │ └── iou2d_calculator.py │ │ ├── coders │ │ │ ├── __init__.py │ │ │ ├── base_bbox_coder.py │ │ │ ├── distance_point_bbox_coder.py │ │ │ └── yolov5_bbox_coder.py │ │ ├── prior_generators │ │ │ ├── __init__.py │ │ │ ├── anchor_generator.py │ │ │ ├── point_generator.py │ │ │ └── utils.py │ │ └── samplers │ │ │ ├── __init__.py │ │ │ ├── base_sampler.py │ │ │ ├── pseudo_sampler.py │ │ │ └── sampling_result.py │ ├── test_time_augs │ │ ├── __init__.py │ │ ├── det_tta.py │ │ └── merge_augs.py │ └── utils │ │ ├── __init__.py │ │ ├── batch_augments.py │ │ ├── data_processor.py │ │ └── misc.py ├── optim │ ├── __init__.py │ └── optimizer │ │ ├── __init__.py │ │ └── lamb.py ├── quantizer │ ├── __init__.py │ └── models │ │ ├── __init__.py │ │ ├── anomaly_quantizer.py │ │ ├── fomo_quantizer.py │ │ ├── pfld_quantizer.py │ │ └── rtmdet_quantizer.py ├── structures │ ├── __init__.py │ ├── bbox │ │ ├── __init__.py │ │ ├── base_boxes.py │ │ ├── bbox_overlaps.py │ │ ├── box_type.py │ │ ├── horizontal_boxes.py │ │ └── transforms.py │ ├── data_sample.py │ ├── mask │ │ ├── __init__.py │ │ ├── mask_target.py │ │ ├── structures.py │ │ └── utils.py │ ├── multlevel_pixel_data.py │ └── utils.py ├── utils │ ├── __init__.py │ ├── colorspace.py │ ├── config.py │ ├── dist_utils.py │ ├── lazy_import.py │ ├── logger.py │ ├── misc.py │ ├── setup_env.py │ ├── simplecv.py │ ├── typing_utils.py │ ├── util_mixins.py │ └── util_random.py ├── version.py └── visualization │ ├── __init__.py │ ├── local_visualizer.py │ ├── palette.py │ ├── simcc_vis.py │ └── visualizer.py ├── tests └── dataset_tests.py └── tools ├── analysis_tools └── vis_scheduler.py ├── dataset_tool ├── read_serial.py └── signal_data_processing.py ├── dist_test.sh ├── dist_train.sh ├── export.py ├── quantization.py ├── slurm_test.sh ├── slurm_train.sh ├── test.py ├── train.py └── vela_config.ini /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "pip" 4 | directory: "requirements/" 5 | schedule: 6 | interval: "monthly" 7 | target-branch: "dev" 8 | labels: 9 | - "pip dependencies" 10 | 11 | - package-ecosystem: "npm" 12 | directory: "/" 13 | schedule: 14 | interval: "monthly" 15 | target-branch: "dev" 16 | labels: 17 | - "npm dependencies" 18 | -------------------------------------------------------------------------------- /.github/workflows/docs-build.yml: -------------------------------------------------------------------------------- 1 | name: docs-build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'docs/**' 9 | - 'package.json' 10 | - 'package-lock.json' 11 | - '.github/workflows/build-docs.yml' 12 | workflow_dispatch: 13 | 14 | permissions: 15 | contents: read 16 | pages: write 17 | id-token: write 18 | 19 | jobs: 20 | build: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: checkout repository 24 | uses: actions/checkout@v4 25 | 26 | - name: setup node 27 | uses: actions/setup-node@v3 28 | with: 29 | node-version: latest 30 | 31 | - name: install dependencies 32 | run: npm install 33 | 34 | - name: build docs 35 | run: npm run docs:build 36 | 37 | - name: upload artifact 38 | uses: actions/upload-pages-artifact@v1 39 | with: 40 | path: docs/.vitepress/dist 41 | 42 | deploy: 43 | environment: 44 | name: github-pages 45 | url: ${{ steps.deployment.outputs.page_url }} 46 | runs-on: ubuntu-latest 47 | needs: build 48 | steps: 49 | - name: deploy to github pages 50 | id: deployment 51 | uses: actions/deploy-pages@v2 52 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: 'Close stale issues and PRs' 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: '0 4 * * *' 7 | 8 | jobs: 9 | stale: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v4 15 | 16 | - name: Checkout script repository 17 | uses: actions/checkout@v4 18 | with: 19 | repository: Seeed-Studio/sync-github-all-issues 20 | path: ci 21 | 22 | - name: Run script 23 | run: ./ci/tools/stale.sh 24 | env: 25 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 26 | -------------------------------------------------------------------------------- /.github/workflows/sync_issues.yml: -------------------------------------------------------------------------------- 1 | name: Automate Issue Management 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | - edited 8 | - assigned 9 | - unassigned 10 | - labeled 11 | - unlabeled 12 | - reopened 13 | 14 | jobs: 15 | add_issue_to_project: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - name: Add issue to GitHub Project 19 | uses: actions/add-to-project@v1.0.2 20 | with: 21 | project-url: https://github.com/orgs/Seeed-Studio/projects/17 22 | github-token: ${{ secrets.ISSUE_ASSEMBLE }} 23 | labeled: bug 24 | label-operator: NOT -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # VS Code 2 | .vscode/** 3 | !.vscode/settings.json 4 | .idea/ 5 | 6 | # macOS 7 | .DS_Store 8 | 9 | # PyTorch model (currently we host the PyTorch models on our server instead of GitHub) 10 | *.pth 11 | 12 | # Byte-compiled / optimized / DLL files 13 | __pycache__/ 14 | *.py[cod] 15 | *$py.class 16 | 17 | # Ruff 18 | .ruff_cache 19 | 20 | # Jupyter 21 | .ipynb_checkpoints 22 | 23 | # Environments 24 | .env 25 | .venv 26 | env/ 27 | venv/ 28 | ENV/ 29 | env.bak/ 30 | venv.bak/ 31 | 32 | # Datasets (and explicitly disignore sscma/datasets) 33 | ./datasets/ 34 | !sscma/datasets 35 | !configs/datasets 36 | 37 | # Work directory 38 | work_dirs/ 39 | 40 | # Python packaging 41 | build/ 42 | dist/ 43 | *.egg-info/ 44 | *.whl 45 | 46 | # Symbolic links of sscma 47 | sscma/configs 48 | sscma/tools 49 | 50 | # NodeJS 51 | node_modules/ 52 | 53 | # Docs (VitepPress) 54 | docs/.vitepress/cache 55 | docs/.vitepress/dist 56 | 57 | # sscma examples 58 | examples/ 59 | work_dir/ 60 | data/ 61 | 62 | # numpy 63 | *.npy 64 | 65 | # datasets 66 | datasets/ 67 | 68 | # quantization 69 | out/ -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include requirements/*.txt 2 | include sscma/.mim/model-index.yml 3 | include sscma/.mim/demo/*/* 4 | recursive-include sscma/.mim/configs *.py *.yml 5 | recursive-include sscma/.mim/tools *.sh *.py 6 | -------------------------------------------------------------------------------- /configs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Studio/ModelAssistant/aa7f20eaea560cd8c290074a331625d6a9e92de3/configs/__init__.py -------------------------------------------------------------------------------- /configs/_base_/default_runtime.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from mmengine.hooks import ( 3 | CheckpointHook, 4 | DistSamplerSeedHook, 5 | IterTimerHook, 6 | LoggerHook, 7 | ParamSchedulerHook, 8 | ) 9 | from mmengine.visualization import LocalVisBackend, TensorboardVisBackend 10 | from mmengine.runner import LogProcessor 11 | from sscma.visualization import UniversalVisualizer 12 | 13 | # configure default hooks 14 | default_hooks = dict( 15 | # record the time of every iteration. 16 | timer=dict(type=IterTimerHook), 17 | # print log every 100 iterations. 18 | logger=dict(type=LoggerHook, interval=100), 19 | # enable the parameter scheduler. 20 | param_scheduler=dict(type=ParamSchedulerHook), 21 | # save checkpoint per epoch. 22 | checkpoint=dict(type=CheckpointHook, interval=5), 23 | # set sampler seed in distributed evrionment. 24 | sampler_seed=dict(type=DistSamplerSeedHook), 25 | ) 26 | 27 | # configure environment 28 | env_cfg = dict( 29 | # whether to enable cudnn benchmark 30 | cudnn_benchmark=True, 31 | # set multi process parameters 32 | mp_cfg=dict(mp_start_method='fork', opencv_num_threads=0), 33 | # set distributed parameters 34 | dist_cfg=dict(backend="nccl"), 35 | ) 36 | 37 | # set visualizer 38 | vis_backends = [dict(type=LocalVisBackend), dict(type=TensorboardVisBackend)] 39 | visualizer = dict(type=UniversalVisualizer, vis_backends=vis_backends) 40 | 41 | # set log level 42 | log_level = "INFO" 43 | log_processor = dict(type=LogProcessor, window_size=50, by_epoch=True) 44 | 45 | # load from which checkpoint 46 | load_from = None 47 | 48 | # whether to resume training from the loaded checkpoint 49 | resume = False 50 | 51 | # Defaults to use random seed and disable `deterministic` 52 | randomness = dict(seed=None, deterministic=False) 53 | 54 | # Do not need to specify default_scope with new config. Therefore set it to 55 | # None to avoid BC-breaking. 56 | default_scope = None 57 | 58 | #whether to dump the config file when starting the script 59 | dump_config = False -------------------------------------------------------------------------------- /configs/_base_/schedules/AdamW_linear_coslr_bs2048.py: -------------------------------------------------------------------------------- 1 | from mmengine.optim import LinearLR 2 | from mmengine.optim import CosineAnnealingLR 3 | from torch.optim import AdamW 4 | 5 | # optimizer 6 | # for batch in each gpu is 256, 1 gpu 7 | # lr = 5e-5 * 256 / 512 = 2.5e-5 8 | optim_wrapper = dict( 9 | optimizer=dict(type=AdamW, lr=2.5e-5, eps=1e-7, weight_decay=0.1), 10 | # specific to vit pretrain 11 | paramwise_cfg=dict( 12 | custom_keys={ 13 | ".cls_token": dict(decay_mult=0.0), 14 | ".pos_embed": dict(decay_mult=0.0), 15 | } 16 | ), 17 | ) 18 | 19 | # learning policy 20 | warmup_epochs = 20 21 | param_scheduler = [ 22 | # warm up learning rate scheduler 23 | dict( 24 | type=LinearLR, 25 | start_factor=4e-4, 26 | by_epoch=True, 27 | begin=1, 28 | end=warmup_epochs, 29 | # update by iter 30 | convert_to_iter_based=True, 31 | ), 32 | # main learning rate scheduler 33 | dict(type=CosineAnnealingLR, eta_min=1e-8, by_epoch=True, begin=warmup_epochs), 34 | ] 35 | 36 | # train, val, test setting 37 | train_cfg = dict(by_epoch=True, max_epochs=100, val_interval=1) 38 | val_cfg = dict() 39 | test_cfg = dict() 40 | 41 | # NOTE: `auto_scale_lr` is for automatically scaling LR, 42 | # based on the actual training batch size. 43 | auto_scale_lr = dict(base_batch_size=256) 44 | -------------------------------------------------------------------------------- /configs/_base_/schedules/schedule_1x.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from mmengine.optim.optimizer.optimizer_wrapper import OptimWrapper 3 | from mmengine.optim.scheduler.lr_scheduler import LinearLR, MultiStepLR 4 | from mmengine.runner.loops import EpochBasedTrainLoop, TestLoop, ValLoop 5 | from torch.optim.sgd import SGD 6 | 7 | # training schedule for 1x 8 | train_cfg = dict(type=EpochBasedTrainLoop, max_epochs=12, val_interval=1) 9 | val_cfg = dict(type=ValLoop) 10 | test_cfg = dict(type=TestLoop) 11 | 12 | # learning rate 13 | param_scheduler = [ 14 | dict(type=LinearLR, start_factor=0.001, by_epoch=False, begin=0, end=500), 15 | dict( 16 | type=MultiStepLR, begin=0, end=12, by_epoch=True, milestones=[8, 11], gamma=0.1 17 | ), 18 | ] 19 | 20 | # optimizer 21 | optim_wrapper = dict( 22 | type=OptimWrapper, 23 | optimizer=dict(type=SGD, lr=0.02, momentum=0.9, weight_decay=0.0001), 24 | ) 25 | 26 | # Default setting for scaling LR automatically 27 | # - `enable` means enable scaling LR automatically 28 | # or not by default. 29 | # - `base_batch_size` = (8 GPUs) x (2 samples per GPU). 30 | auto_scale_lr = dict(enable=False, base_batch_size=16) 31 | -------------------------------------------------------------------------------- /configs/_base_/schedules/sgd_linear_coslr_bs2048.py: -------------------------------------------------------------------------------- 1 | from mmengine.optim import LinearLR 2 | from mmengine.optim import CosineAnnealingLR 3 | from torch.optim import SGD 4 | 5 | # optimizer 6 | # In ClassyVision, the lr is set to 0.003 for bs4096. 7 | # In this implementation(bs2048), lr = 0.003 / 4096 * (32bs * 64gpus) = 0.0015 8 | optim_wrapper = dict( 9 | optimizer=dict(type=SGD, lr=0.01, weight_decay=0.0005, momentum=0.937), 10 | # specific to vit pretrain 11 | paramwise_cfg=dict( 12 | custom_keys={ 13 | ".cls_token": dict(decay_mult=0.0), 14 | ".pos_embed": dict(decay_mult=0.0), 15 | } 16 | ), 17 | ) 18 | 19 | # learning policy 20 | warmup_epochs = 3 # about 10000 iterations for ImageNet-1k 21 | param_scheduler = [ 22 | # warm up learning rate scheduler 23 | dict( 24 | type=LinearLR, 25 | start_factor=0.3333, 26 | by_epoch=True, 27 | begin=0, 28 | end=warmup_epochs, 29 | # update by iter 30 | convert_to_iter_based=True, 31 | ), 32 | # main learning rate scheduler 33 | dict(type=CosineAnnealingLR, eta_min=0.0002, by_epoch=True, begin=warmup_epochs), 34 | ] 35 | 36 | # train, val, test setting 37 | train_cfg = dict(by_epoch=True, max_epochs=100, val_interval=1) 38 | val_cfg = dict() 39 | test_cfg = dict() 40 | 41 | # NOTE: `auto_scale_lr` is for automatically scaling LR, 42 | # based on the actual training batch size. 43 | auto_scale_lr = dict(base_batch_size=2048) 44 | -------------------------------------------------------------------------------- /configs/anomaly/vae_mirophone.py: -------------------------------------------------------------------------------- 1 | from mmengine.config import read_base 2 | 3 | with read_base(): 4 | from .._base_.default_runtime import * 5 | 6 | from sscma.models.detectors.anomaly import Vae_Model 7 | from sscma.models import Vae_Encode, Vae_Decode, Conv_block2D 8 | from torch.nn import MSELoss 9 | from sscma.datasets import Microphone_dataset 10 | from sscma.evaluation import MseMetric 11 | from sscma.deploy.models.anomaly_infer import AnomalyInfer 12 | from sscma.quantizer.models import AnomalyQuantModel 13 | 14 | dataset_type = Microphone_dataset 15 | 16 | data_root = "./datasets" 17 | train_data_prefix = "train" 18 | val_data_prefix = "val" 19 | 20 | epochs=100 21 | imgsz = (32, 32) 22 | batch_size = 1 23 | 24 | model = model = dict( 25 | type=Vae_Model, 26 | encode=dict( 27 | type=Vae_Encode, x_size=32, in_channel=3, out_channel=8, conv=Conv_block2D 28 | ), 29 | decode=dict( 30 | type=Vae_Decode, 31 | x_size=32, 32 | out_channel=8, 33 | in_channel=3, 34 | conv=Conv_block2D, 35 | ), 36 | loss=dict(type=MSELoss), 37 | freeze_randn=None, 38 | ) 39 | 40 | 41 | deploy = dict(type=AnomalyInfer) 42 | quantizer_config=dict( 43 | type=AnomalyQuantModel, 44 | ) 45 | 46 | train_dataloader = dict( 47 | batch_size=batch_size, 48 | num_workers=1, 49 | persistent_workers=True, 50 | drop_last=True, 51 | # collate_fn=dict(type=default_collate), 52 | dataset=dict(type=dataset_type, data_root=data_root, data_prefix=train_data_prefix), 53 | ) 54 | val_dataloader = dict( 55 | batch_size=batch_size, 56 | num_workers=1, 57 | persistent_workers=True, 58 | drop_last=True, 59 | # collate_fn=dict(type=default_collate), 60 | dataset=dict(type=dataset_type, data_root=data_root, data_prefix=val_data_prefix), 61 | ) 62 | test_dataloader = val_dataloader 63 | 64 | train_cfg = dict(by_epoch=True, max_epochs=epochs) 65 | val_cfg = dict() 66 | test_cfg = dict() 67 | 68 | val_evaluator = dict(type=MseMetric) 69 | test_evaluator = val_evaluator 70 | 71 | from torch.optim import Adam 72 | 73 | optim_wrapper = dict(optimizer=dict(type=Adam, lr=1e-4, weight_decay=5e-4)) 74 | -------------------------------------------------------------------------------- /configs/cls/timm_classify.py: -------------------------------------------------------------------------------- 1 | from sscma.models import TimmBackbone, CrossEntropyLoss, Mixup, CutMix 2 | 3 | 4 | # model settings 5 | data_preprocessor = dict( 6 | num_classes=7, 7 | # RGB format normalization parameters 8 | mean=[123.675, 116.28, 103.53], 9 | std=[58.395, 57.12, 57.375], 10 | # convert image from BGR to RGB 11 | to_rgb=True, 12 | ) 13 | 14 | 15 | model = dict( 16 | data_preprocessor=data_preprocessor, 17 | type=TimmBackbone, 18 | pretrained=True, 19 | num_classes=7, 20 | loss=dict( 21 | type=CrossEntropyLoss, 22 | loss_weight=1.0, 23 | ), 24 | train_cfg=dict( 25 | augments=[dict(type=Mixup, alpha=0.8), dict(type=CutMix, alpha=1.0)] 26 | ), 27 | ) 28 | -------------------------------------------------------------------------------- /configs/datasets/imagenet_bs32.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from sscma.datasets import LanceDataset 3 | from sscma.datasets.transforms import ( 4 | RandomFlip, 5 | RandomResizedCrop, 6 | RandomFlip, 7 | ResizeEdge, 8 | CenterCrop, 9 | PackInputs, 10 | RandomResizedCrop, 11 | ResizeEdge, 12 | CenterCrop, 13 | ) 14 | from mmengine.dataset import DefaultSampler 15 | from sscma.evaluation import Accuracy 16 | 17 | # dataset settings 18 | dataset_type = LanceDataset 19 | 20 | data_root='datasets/imagenet' 21 | 22 | train_pipeline = [ 23 | # dict(type=LoadImageFromFile,imdecode_backend='cv2'), 24 | dict(type=RandomResizedCrop, scale=224), 25 | dict(type=RandomFlip, prob=0.5, direction="horizontal"), 26 | dict(type=PackInputs), 27 | ] 28 | 29 | test_pipeline = [ 30 | # dict(type=LoadImageFromFile,imdecode_backend='cv2'), 31 | dict(type=ResizeEdge, scale=256, edge="short"), 32 | dict(type=CenterCrop, crop_size=224), 33 | dict(type=PackInputs), 34 | ] 35 | 36 | train_dataloader = dict( 37 | batch_size=32, 38 | num_workers=2, 39 | pin_memory=True, 40 | dataset=dict( 41 | type=dataset_type, 42 | data_root=data_root, 43 | data_prefix=dict(img_path="train"), 44 | pipeline=train_pipeline, 45 | ), 46 | sampler=dict(type=DefaultSampler, shuffle=True), 47 | ) 48 | 49 | val_dataloader = dict( 50 | batch_size=32, 51 | num_workers=2, 52 | pin_memory=True, 53 | dataset=dict( 54 | type=dataset_type, 55 | data_root=data_root, 56 | data_prefix=dict(img_path="valid"), 57 | pipeline=test_pipeline, 58 | ), 59 | sampler=dict(type=DefaultSampler, shuffle=False), 60 | ) 61 | val_evaluator = dict(type=Accuracy, topk=(1, 5)) 62 | 63 | # If you want standard test, please manually configure the test dataset 64 | test_dataloader = val_dataloader 65 | test_evaluator = val_evaluator 66 | -------------------------------------------------------------------------------- /configs/datasets/lancedb_bs32.py: -------------------------------------------------------------------------------- 1 | from sscma.datasets import LanceDataset 2 | from sscma.datasets.transforms import ( 3 | RandomResizedCrop, 4 | RandomFlip, 5 | ResizeEdge, 6 | CenterCrop, 7 | PackInputs, 8 | ) 9 | from mmengine.dataset import DefaultSampler 10 | from sscma.evaluation import Accuracy 11 | 12 | # dataset settings 13 | dataset_type = LanceDataset 14 | 15 | data_root = "/home/dq/datasets/imagenet100/" 16 | 17 | train_pipeline = [ 18 | dict(type=RandomResizedCrop, scale=224), 19 | dict(type=RandomFlip, prob=0.5, direction="horizontal"), 20 | dict(type=PackInputs), 21 | ] 22 | 23 | test_pipeline = [ 24 | dict(type=ResizeEdge, scale=256, edge="short"), 25 | dict(type=CenterCrop, crop_size=224), 26 | dict(type=PackInputs), 27 | ] 28 | 29 | train_dataloader = dict( 30 | batch_size=64, 31 | num_workers=8, 32 | pin_memory=True, 33 | dataset=dict( 34 | type=dataset_type, 35 | data_root=data_root, 36 | data_prefix=dict(img_path="train"), 37 | pipeline=train_pipeline, 38 | ), 39 | sampler=dict(type=DefaultSampler, shuffle=True), 40 | persistent_workers=True 41 | ) 42 | 43 | val_dataloader = dict( 44 | batch_size=64, 45 | num_workers=8, 46 | pin_memory=True, 47 | dataset=dict( 48 | type=dataset_type, 49 | data_root=data_root, 50 | data_prefix=dict(img_path="val"), 51 | pipeline=test_pipeline, 52 | ), 53 | sampler=dict(type=DefaultSampler, shuffle=False), 54 | persistent_workers=True 55 | ) 56 | val_evaluator = dict(type=Accuracy, topk=(1, 5)) 57 | 58 | # If you want standard test, please manually configure the test dataset 59 | test_dataloader = val_dataloader 60 | test_evaluator = val_evaluator 61 | -------------------------------------------------------------------------------- /configs/pretrain/default_runtime.py: -------------------------------------------------------------------------------- 1 | # defaults to use registries in mmpretrain 2 | from mmengine.hooks import ( 3 | IterTimerHook, 4 | LoggerHook, 5 | ParamSchedulerHook, 6 | CheckpointHook, 7 | DistSamplerSeedHook, 8 | ) 9 | from mmengine.hooks.visualization_hook import VisualizationHook 10 | from mmengine.visualization import LocalVisBackend 11 | from sscma.visualization import UniversalVisualizer 12 | 13 | # configure default hooks 14 | default_hooks = dict( 15 | # record the time of every iteration. 16 | timer=dict(type=IterTimerHook), 17 | # print log every 100 iterations. 18 | logger=dict(type=LoggerHook, interval=100), 19 | # enable the parameter scheduler. 20 | param_scheduler=dict(type=ParamSchedulerHook), 21 | # save checkpoint per epoch. 22 | checkpoint=dict(type=CheckpointHook, interval=1), 23 | # set sampler seed in distributed evrionment. 24 | sampler_seed=dict(type=DistSamplerSeedHook), 25 | # validation results visualization, set True to enable it. 26 | visualization=dict(type=VisualizationHook, enable=False), 27 | ) 28 | 29 | # configure environment 30 | env_cfg = dict( 31 | # whether to enable cudnn benchmark 32 | cudnn_benchmark=False, 33 | # set multi process parameters 34 | mp_cfg=dict(mp_start_method="fork", opencv_num_threads=0), 35 | # set distributed parameters 36 | dist_cfg=dict(backend="nccl"), 37 | ) 38 | 39 | # set visualizer 40 | vis_backends = [dict(type=LocalVisBackend)] 41 | visualizer = dict(type=UniversalVisualizer, vis_backends=vis_backends) 42 | 43 | # set log level 44 | log_level = "INFO" 45 | 46 | # load from which checkpoint 47 | load_from = None 48 | 49 | # whether to resume training from the loaded checkpoint 50 | resume = False 51 | 52 | dump_config = False 53 | 54 | # Defaults to use random seed and disable `deterministic` 55 | randomness = dict(seed=None, deterministic=False) 56 | -------------------------------------------------------------------------------- /configs/pretrain/imagenet_bs2048_rsb.py: -------------------------------------------------------------------------------- 1 | from sscma.optim import Lamb 2 | from mmengine.optim import LinearLR,CosineAnnealingLR 3 | # optimizer 4 | optim_wrapper = dict(optimizer=dict(type=Lamb, lr=0.005, weight_decay=0.02)) 5 | 6 | # learning policy 7 | param_scheduler = [ 8 | # warm up learning rate scheduler 9 | dict( 10 | type=LinearLR, 11 | start_factor=0.0001, 12 | by_epoch=True, 13 | begin=0, 14 | end=5, 15 | # update by iter 16 | convert_to_iter_based=True), 17 | # main learning rate scheduler 18 | dict( 19 | type=CosineAnnealingLR, 20 | T_max=95, 21 | eta_min=1.0e-6, 22 | by_epoch=True, 23 | begin=5, 24 | end=100) 25 | ] 26 | 27 | # train, val, test setting 28 | train_cfg = dict(by_epoch=True, max_epochs=600, val_interval=1) 29 | val_cfg = dict() 30 | test_cfg = dict() 31 | 32 | # NOTE: `auto_scale_lr` is for automatically scaling LR, 33 | # based on the actual training batch size. 34 | auto_scale_lr = dict(base_batch_size=2048) -------------------------------------------------------------------------------- /configs/pretrain/mobilenetv4_imagenet.py: -------------------------------------------------------------------------------- 1 | from mmengine.config import read_base 2 | 3 | with read_base(): 4 | from ..datasets.lancedb_bs32 import * 5 | from .._base_.default_runtime import * 6 | from ..cls.timm_classify import * 7 | from .._base_.schedules.AdamW_linear_coslr_bs2048 import * 8 | 9 | train_dataloader.batch_size=64 10 | auto_scale_lr = dict(base_batch_size=64) 11 | model.model_name="mobilenetv4_hybrid_medium" -------------------------------------------------------------------------------- /configs/pretrain/resnet50_imagenet.py: -------------------------------------------------------------------------------- 1 | from mmengine.config import read_base 2 | 3 | with read_base(): 4 | from ..datasets.imagenet_bs32 import * 5 | from .._base_.default_runtime import * 6 | from ..cls.timm_classify import * 7 | from .._base_.schedules.sgd_linear_coslr_bs2048 import * 8 | 9 | model.model_name = "resnet50" 10 | -------------------------------------------------------------------------------- /docs/.vitepress/config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitepress' 2 | 3 | import { withMermaid } from 'vitepress-plugin-mermaid' 4 | 5 | import en_US from './locales/en_US' 6 | import zh_CN from './locales/zh_CN' 7 | 8 | 9 | export default withMermaid(defineConfig({ 10 | base: '/', 11 | title: 'SSCMA', 12 | lastUpdated: true, 13 | cleanUrls: true, 14 | 15 | head: [ 16 | ['link', { rel: 'icon', type: 'image/png', href: '/favicon.png' }], 17 | ['meta', { property: 'og:type', content: 'website' }], 18 | ['meta', { property: 'og:title', content: 'SSCMA' }], 19 | ['meta', { property: 'og:image', content: '/og-image.png' }], 20 | ['meta', { property: 'og:url', content: 'https://github.com/Seeed-Studio/ModelAssistant' }], 21 | ['meta', { property: 'og:description', content: 'Seeed SenSeCraft Model Assiant is an open-source project focused on embedded AI.' }], 22 | ['meta', { name: 'twitter:card', content: 'summary_large_image' }], 23 | ['meta', { name: 'twitter:site', content: '@seeedstudio' }], 24 | ['meta', { name: 'theme-color', content: '#051726' }] 25 | ], 26 | 27 | locales: { 28 | root: { 29 | label: 'English', 30 | lang: en_US.lang, 31 | description: en_US.description, 32 | themeConfig: en_US.themeConfig 33 | }, 34 | zh_cn: { 35 | label: '简体中文', 36 | lang: zh_CN.lang, 37 | description: zh_CN.description, 38 | themeConfig: zh_CN.themeConfig 39 | } 40 | }, 41 | 42 | themeConfig: { 43 | search: { provider: 'local' }, 44 | 45 | i18nRouting: true, 46 | outline: [2, 3], 47 | 48 | socialLinks: [ 49 | { icon: 'github', link: 'https://github.com/Seeed-Studio/ModelAssistant' } 50 | ] 51 | } 52 | })) 53 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | // https://vitepress.dev/guide/custom-theme 2 | import { h } from 'vue' 3 | import Theme from 'vitepress/theme' 4 | import './style.css' 5 | 6 | export default { 7 | ...Theme, 8 | Layout: () => { 9 | return h(Theme.Layout, null, { 10 | // https://vitepress.dev/guide/extending-default-theme#layout-slots 11 | }) 12 | }, 13 | enhanceApp({ app, router, siteData }) { 14 | // ... 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docs/community/contributing.md: -------------------------------------------------------------------------------- 1 | # Contribute 2 | 3 | Contributions to [SSCMA](https://github.com/Seeed-Studio/ModelAssistant) are welcome! We welcome contributions of any kind, including but not limited to: 4 | 5 | - Fixing bugs 6 | 7 | The steps for fixing code implementation errors are as follows: 8 | 9 | If the submitted code changes are large, it is recommended to submit an issue first, and correctly describe the phenomenon of the issue, the cause and the way to reproduce it, and discuss and confirm the fixing plan. 10 | 11 | Fix the bug and add the corresponding unit test, and submit the pull request. 12 | 13 | - New feature or component 14 | 15 | If a new feature or module involves large code changes, it is recommended to submit an issue first to confirm the necessity of the feature. 16 | 17 | Implement the new feature, add unit tests, and submit a pull request. 18 | 19 | - Documentation additions 20 | 21 | Fix the documentation to submit a pull request directly. 22 | 23 | The steps to add documentation or translate it into another language are as follows. 24 | 25 | Submit an issue to confirm the need to add the documentation. 26 | 27 | ## How to Contribute 28 | 29 | Please refer to the [Github Documentation - Collaborating](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests). 30 | 31 | ## Commit Style 32 | 33 | We recommend that you follow the following authoring principles when writing your commit, as this will make our project cleaner and easier to iterate. 34 | 35 | ``` 36 | build: build related changes 37 | chore: typo fixes, library updates, etc. 38 | ci: continue integration related changes 39 | deps: dependencies update 40 | docs: docs related changes 41 | feat: new feactures 42 | fix: fix issues 43 | perf: add perf results 44 | refactor: refactor components 45 | revert: undo some changes 46 | style: code style changes 47 | test: test cases changes 48 | ``` 49 | 50 | ## Permissions Section 51 | 52 | After a contribution is submitted, you agree to the project's [License](./license). 53 | -------------------------------------------------------------------------------- /docs/community/faqs.md: -------------------------------------------------------------------------------- 1 | # FAQs 2 | 3 | If you encounter any problems using SSCMA, submitting your questions on [SSCMA Issues](https://github.com/Seeed-Studio/ModelAssistant/issues) is welcomed, and we will reply to you as soon as possible. We also organize some common problems so that you can find solutions in a timely manner. 4 | -------------------------------------------------------------------------------- /docs/community/reference.md: -------------------------------------------------------------------------------- 1 | # Reference 2 | 3 | [SSCMA](https://github.com/Seeed-Studio/ModelAssistant) referenced the following projects: 4 | 5 | - [OpenMMLab](https://openmmlab.com/) 6 | - [ONNX](https://github.com/onnx/onnx) 7 | - [NCNN](https://github.com/Tencent/ncnn) 8 | -------------------------------------------------------------------------------- /docs/datasets/images/labelme_pfld_1p.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Studio/ModelAssistant/aa7f20eaea560cd8c290074a331625d6a9e92de3/docs/datasets/images/labelme_pfld_1p.png -------------------------------------------------------------------------------- /docs/datasets/images/roboflow_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Studio/ModelAssistant/aa7f20eaea560cd8c290074a331625d6a9e92de3/docs/datasets/images/roboflow_download.png -------------------------------------------------------------------------------- /docs/datasets/images/roboflow_fomo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Studio/ModelAssistant/aa7f20eaea560cd8c290074a331625d6a9e92de3/docs/datasets/images/roboflow_fomo.png -------------------------------------------------------------------------------- /docs/datasets/images/roboflow_generate_version.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Studio/ModelAssistant/aa7f20eaea560cd8c290074a331625d6a9e92de3/docs/datasets/images/roboflow_generate_version.png -------------------------------------------------------------------------------- /docs/images/rtmdet_coco_eval.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Studio/ModelAssistant/aa7f20eaea560cd8c290074a331625d6a9e92de3/docs/images/rtmdet_coco_eval.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # https://vitepress.dev/reference/default-theme-home-page 3 | layout: home 4 | 5 | hero: 6 | name: SSCMA 7 | text: Open-source project focused on embedded AI. 8 | tagline: We have optimized excellent algorithms for real-world scenarios and made implementation more user-friendly, achieving faster and more accurate inference on embedded devices. 9 | image: 10 | src: https://files.seeedstudio.com/sscma/docs/images/SSCMA-Hero.png 11 | alt: SSCMA 12 | actions: 13 | - theme: brand 14 | text: Get Started 15 | link: ./introduction/quick_start 16 | - theme: alt 17 | text: Install SSCMA 18 | link: ./introduction/installation 19 | - theme: alt 20 | text: View on GitHub 21 | link: https://github.com/Seeed-Studio/ModelAssistant 22 | 23 | features: 24 | - icon: 🔍 25 | title: Anomaly Detection 26 | details: In the real world, anomalous data is often difficult to identify, and even if it can be identified, it requires a very high cost. The anomaly detection algorithm collects normal data in a low-cost way, and anything outside normal data is considered anomalous. 27 | - icon: 👁️ 28 | title: Computer Vision 29 | details: Here we provide a number of computer vision algorithms such as object detection, image classification, image segmentation and pose estimation. We optimizes these computer vision algorithms to achieve good running speed and accuracy in low-end devices. 30 | - icon: ⏱️ 31 | title: Scenario Specific 32 | details: SSCMA provides customized scenarios for specific production environments, such as identification of analog instruments, traditional digital meters, and audio classification. We will continue to add more algorithms for specified scenarios in the future. 33 | --- 34 | -------------------------------------------------------------------------------- /docs/tutorials/deploy/grove_vision_ai_v2.md: -------------------------------------------------------------------------------- 1 | # Deploying SSCMA Models on Grove Vision AI V2 2 | 3 | This example is a deployment tutorial for models included in [SSCMA](https://github.com/Seeed-Studio/ModelAssistant) on the Grove Vision AI V2 module. 4 | 5 | To deploy SSCMA models on the Grove Vision AI V2, you need to first convert the models into quantized TensorFlow Lite format. After conversion with the Vela converter, you can deploy the models to the Grove Vision AI V2 module. SSCMA has added this feature in the export tool, allowing you to add the parameter `--format vela` during the export process to export Vela models. Once successfully exported, the remaining steps are consistent with [SSCMA - Model Deployment](overview). 6 | 7 | Additionally, you can attempt to manually build the firmware to accommodate the model. 8 | 9 | ## Building Firmware in Linux Environment 10 | 11 | The following steps have been tested on Ubuntu 20.04 PC. 12 | 13 | ### Install Dependencies 14 | 15 | ```bash 16 | sudo apt install make 17 | ``` 18 | 19 | ### Download Arm GNU Toolchain 20 | 21 | ```bash 22 | cd ~ 23 | wget https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-eabi.tar.xz 24 | ``` 25 | 26 | ### Extract the File 27 | 28 | ```bash 29 | tar -xvf arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-eabi.tar.xz 30 | ``` 31 | 32 | ### Add to PATH 33 | 34 | ```bash 35 | export PATH="$HOME/arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi/bin/:$PATH" 36 | ``` 37 | 38 | ### Clone the Following Repository and Enter the Seeed_Grove_Vision_AI_Module_V2 Folder 39 | 40 | ```bash 41 | git clone --recursive https://github.com/HimaxWiseEyePlus/Seeed_Grove_Vision_AI_Module_V2.git 42 | cd Seeed_Grove_Vision_AI_Module_V2 43 | ``` 44 | 45 | ### Compile the Firmware 46 | 47 | ```bash 48 | cd EPII_CM55M_APP_S 49 | make clean 50 | make 51 | ``` 52 | 53 | The output ELF file is located at `/obj_epii_evb_icv30_bdv10/gnu_epii_evb_WLCSP65/EPII_CM55M_gnu_epii_evb_WLCSP65_s.elf`. 54 | 55 | ### Generate Firmware Image File 56 | 57 | ```bash 58 | cd ../we2_image_gen_local/ 59 | cp ../EPII_CM55M_APP_S/obj_epii_evb_icv30_bdv10/gnu_epii_evb_WLCSP65/EPII_CM55M_gnu_epii_evb_WLCSP65_s.elf input_case1_secboot/ 60 | ./we2_local_image_gen project_case1_blp_wlcsp.json 61 | ``` 62 | 63 | The output firmware image is located at `./output_case1_sec_wlcsp/output.img`. 64 | 65 | ### Flashing the Firmware 66 | 67 | You can use the SSCMA Web Toolkit or Grove Vision AI V2's USB-to-serial tool to flash the firmware, or you can directly use the Xmodem protocol to flash the firmware. 68 | -------------------------------------------------------------------------------- /docs/tutorials/deploy/images/sscma-upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Studio/ModelAssistant/aa7f20eaea560cd8c290074a331625d6a9e92de3/docs/tutorials/deploy/images/sscma-upload.png -------------------------------------------------------------------------------- /docs/zh_cn/community/contributing.md: -------------------------------------------------------------------------------- 1 | # 贡献指南 2 | 3 | 欢迎对 [SSCMA](https://github.com/Seeed-Studio/ModelAssistant) 做出贡献!我们欢迎任何类型的贡献,包括不限于: 4 | 5 | - 修复错误 6 | 7 | 修复代码实现错误的步骤如下: 8 | 9 | 如果提交的代码改动较大,建议先提交 Issue,并正确描述 Issue 的现象、原因和复现方式,讨论后确认修复方案。 10 | 11 | 修复错误并补充相应的单元测试,提交拉取请求。 12 | 13 | - 新增功能或组件 14 | 15 | 如果新功能或模块涉及较大的代码改动,建议先提交 Issue,确认功能的必要性。 16 | 17 | 实现新增功能并添单元测试,提交拉取请求。 18 | 19 | - 文档补充 20 | 21 | 修复文档可以直接提交拉取请求。 22 | 23 | 添加文档或将文档翻译成其他语言步骤如下: 24 | 25 | 提交 Issue,确认添加文档的必要性。 26 | 27 | ## 如何贡献 28 | 29 | 请参考 [Github 文档 - Collaborating](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests)。 30 | 31 | ## 提交格式 32 | 33 | 我们建议您在编写 Commit 时,遵循以下编写原则,这可以让我们的项目更加整洁,方便迭代 34 | 35 | ``` 36 | build: build related changes 37 | chore: typo fixes, library updates, etc. 38 | ci: continue integration related changes 39 | deps: dependencies update 40 | docs: docs related changes 41 | feat: new feactures 42 | fix: fix issues 43 | perf: add perf results 44 | refactor: refactor components 45 | revert: undo some changes 46 | style: code style changes 47 | test: test cases changes 48 | ``` 49 | 50 | ## 许可部分 51 | 52 | 在贡献被提交之后,我们默认您同意了本项目的[许可协议](./license)。 53 | -------------------------------------------------------------------------------- /docs/zh_cn/community/faqs.md: -------------------------------------------------------------------------------- 1 | # FAQs 2 | 3 | 如果您在使用 [SSCMA](https://github.com/Seeed-Studio/ModelAssistant) 时遇到任何问题,欢迎在 [SSCMA Issues](https://github.com/Seeed-Studio/ModelAssistant/issues) 上提交您的问题,我们将尽快回复您。我们还整理了一些常见问题,以便您能够及时找到解决方案。 4 | -------------------------------------------------------------------------------- /docs/zh_cn/community/reference.md: -------------------------------------------------------------------------------- 1 | # 参考文档 2 | 3 | [SSCMA](https://github.com/Seeed-Studio/ModelAssistant) 参考了以下项目: 4 | 5 | - [OpenMMLab](https://openmmlab.com/) 6 | - [ONNX](https://github.com/onnx/onnx) 7 | - [NCNN](https://github.com/Tencent/ncnn) 8 | -------------------------------------------------------------------------------- /docs/zh_cn/custom/basics.md: -------------------------------------------------------------------------------- 1 | # 基础配置结构 2 | 3 | SSCMA 使用的配置文件位于 `configs` 目录下,用于不同任务下不同模型的训练。我们在其根据不同的任务分类划分了子文件夹,在各个子文件夹中,保存有多个模型的不同训练管线参数。 4 | 5 | :::tip 6 | 7 | 其中名为 `_base_` 的任务文件夹是我们其他任务的继承对象,关于配置文件继承的详细说明,请参考 [MMEngine - 配置文件的继承](https://mmengine.readthedocs.io/zh_CN/latest/advanced_tutorials/config.html#id3)。 8 | 9 | ::: 10 | 11 | 我们使用 Python 的字典和列表来定义模型、数据加载、训练和评估等各个部分的参数。以下是该配置文件的结构和各个部分的作用,以及一般需要调整的参数。 12 | 13 | ## 导入模块 14 | 15 | 在开始构建你的配置文件之前,首先需要导入必要的模块,如下所示: 16 | 17 | ```python 18 | import torch.nn as nn 19 | from mmengine.registry import OPTIMIZERS 20 | ``` 21 | 22 | ## 模型配置 23 | 24 | - `num_classes`:类别数量,对于口罩检测,通常是 2(戴口罩和不戴口罩)。 25 | - `widen_factor`:模型宽度因子,用于调整模型的宽度。 26 | 27 | ## 数据配置 28 | 29 | - `dataset_type`:指定数据集类型。 30 | - `data_root`:数据集的根目录。 31 | - `train_ann`、`train_data`、`val_ann`、`val_data`:训练和验证数据的注释文件和数据目录。 32 | - `height`、`width`、`imgsz`:输入图像的尺寸。 33 | 34 | ## 训练配置 35 | 36 | - `batch`、`workers`、`persistent_workers`:训练时的批处理大小、工作线程数和持久工作线程。 37 | - `val_batch`、`val_workers`:验证时的批处理大小和工作线程数。 38 | - `lr`、`epochs`、`weight_decay`、`momentum`:学习率、训练周期、权重衰减和动量。 39 | 40 | ## 钩子 41 | 42 | - `default_hooks`:定义了训练过程中的钩子,如可视化钩子。 43 | - `visualizer`:定义了可视化器。 44 | 45 | ## 数据预处理 46 | 47 | - `data_preprocessor`:定义了数据预处理的参数,如均值、标准差、颜色转换等。 48 | 49 | ## 模型结构 50 | 51 | 定义了模型的类型、数据预处理器、骨干网络和头部网络的配置。 52 | 53 | ## 部署配置 54 | 55 | - `deploy`:定义了模型部署时的数据预处理器配置。 56 | 57 | ## 图像解码后端 58 | 59 | - `imdecode_backend`:指定图像解码的后端。 60 | 61 | ## 预处理流水线 62 | 63 | - `pre_transform`、`train_pipeline`、`test_pipeline`:定义了训练和测试数据的预处理流水线。 64 | 65 | ## 数据加载器 66 | 67 | - `train_dataloader`、`val_dataloader`、`test_dataloader`:定义了训练、验证和测试数据加载器的配置。 68 | 69 | ## 优化器配置 70 | 71 | - `optim_wrapper`:定义了优化器的类型和参数。 72 | 73 | ## 评估器 74 | 75 | - `val_evaluator`、`test_evaluator`:定义了验证和测试的评估器。 76 | 77 | ## 训练配置 78 | 79 | - `train_cfg`:定义了训练的配置,如是否按周期训练和最大周期数。 80 | 81 | ## 学习策略 82 | 83 | - `param_scheduler`:定义了学习率调度器的策略。 84 | 85 | 配置文件涵盖了从数据预处理到模型训练和评估的各个方面。根据具体的训练需求,可能需要调整的参数包括学习率、批次大小、训练周期、优化器参数、数据增强策略等。这些参数的调整将直接影响模型的性能和训练效果。 -------------------------------------------------------------------------------- /docs/zh_cn/custom/optimizer.md: -------------------------------------------------------------------------------- 1 | # 优化器配置 2 | 3 | 优化器配置文件用于定义模型训练过程中的优化器、优化器封装和学习率调整策略。SSCMA 中基于 MMEngine 的优化器封装是一个高级抽象,在 Pytorch 原生优化器的基础上,增加了功能并提供了统一接口,可支持多种训练策略,如混合精度训练、梯度累加和梯度截断,并定义了参数更新流程,方便实现不同训练策略的切换。 4 | 5 | 优化器配置通常包含以下几个模块: 6 | 7 | 1. **优化器**:优化器是用于更新模型参数以最小化损失函数的算法。常见的优化器包括 SGD、Adam、AdamW 等。 8 | 2. **优化器封装**:定义优化器封装类型,并配置参数调整策略和其他高级功能。 9 | 3. **优化器参数调整策略**:定义动态调整优化器参数的策略,如线性学习率调整、余弦退火学习率调整等。 10 | 11 | 下面以 SSCMA 中 RTMDet 模型的优化器配置为例,介绍优化器配置的详细内容。 12 | 13 | :::tip 14 | 关于优化器的更多信息,请参考 [PyTorch 优化器](https://pytorch.org/docs/stable/optim.html)、[MMEngine - 优化器](https://mmengine.readthedocs.io/zh_CN/latest/tutorials/optimizer.html)。 15 | ::: 16 | 17 | 18 | ## 优化器 19 | 20 | 优化器模块用于定义优化器的类型和参数。 21 | 22 | ```python 23 | optimizer=dict( 24 | type=AdamW, # 优化器类型 25 | lr=base_lr, # 学习率 26 | weight_decay=0.05, # 权重衰减 27 | ) 28 | ``` 29 | 30 | ## 优化器封装 31 | 32 | 优化器封装模块用于进一步配置优化器的参数调整策略和其他高级功能。 33 | 34 | ```python 35 | optim_wrapper=dict( 36 | type=OptimWrapper, # 优化器封装类型 37 | optimizer=dict(type=AdamW, lr=base_lr, weight_decay=0.05), # 优化器配置 38 | paramwise_cfg=dict( 39 | norm_decay_mult=0, # 归一化层权重衰减倍数 40 | bias_decay_mult=0, # 偏置项权重衰减倍数 41 | bypass_duplicate=True, # 是否绕过重复参数 42 | ), 43 | ) 44 | ``` 45 | 46 | #### 优化器参数调整策略 47 | 48 | 优化器参数调整策略模块用于动态调整优化器的参数。 49 | 50 | ```python 51 | param_scheduler=[ 52 | dict( 53 | type=LinearLR, # 线性学习率调整 54 | start_factor=1.0e-5, # 起始因子 55 | by_epoch=False, # 是否按 epoch 调整 56 | begin=0, # 开始调整的迭代 57 | end=1000, # 结束调整的迭代 58 | ), 59 | dict( 60 | type=CosineAnnealingLR, # 余弦退火学习率调整 61 | eta_min=base_lr * 0.05, # 最小学习率 62 | begin=max_epochs // 2, # 开始调整的 epoch 63 | end=max_epochs, # 结束调整的 epoch 64 | T_max=max_epochs // 2, # 余弦周期 65 | by_epoch=True, # 是否按 epoch 调整 66 | convert_to_iter_based=True, # 是否转换为迭代调整 67 | ), 68 | ] 69 | ``` 70 | -------------------------------------------------------------------------------- /docs/zh_cn/custom/pipelines.md: -------------------------------------------------------------------------------- 1 | # 训练与验证管线 2 | 3 | 在训练与验证过程中,我们需要对数据进行预处理、特征工程、模型训练、模型评估等一系列操作。为了方便用户进行这些操作,我们提供了一套完整的训练与验证管线,用户可以通过简单的配置来完成整个训练与验证过程。 4 | 5 | :::tip 6 | 7 | 在训练流程中,数据集和数据加载器是核心组件,SSCMA 基于 MMEngine,因此这方面与 PyTorch 中的概念相同。数据集负责定义数据量、读取和预处理方式,而数据加载器则负责按批次大小、随机乱序和并行等设置迭代加载数据,形成数据源。更多请参考 [PyTorch 数据加载](https://pytorch.org/docs/stable/data.html)。 8 | 9 | ::: 10 | 11 | 12 | 在 MMEngine 的执行器(Runner)中,你可以通过设置三个特定的参数来指定数据加载器,以便在不同的训练阶段使用: 13 | 14 | - train_dataloader:此参数在执行器的 Runner.train() 方法中调用,负责向模型提供训练所需的数据。 15 | - val_dataloader:此参数在执行器的 Runner.val() 方法中使用,并且会在 Runner.train() 方法的特定训练周期中被调用,用于对模型进行性能验证和评估。 16 | - test_dataloader:此参数在执行器的 Runner.test() 方法中使用,用于对模型进行最终的测试。 17 | 18 | MMEngine 兼容 PyTorch 的原生 DataLoader,这意味着你可以直接将已经创建的 DataLoader 实例传递给上述三个参数。 19 | 20 | 训练 Pipeline 配置通常包含以下几个步骤: 21 | 22 | 1. **加载图像**:定义从文件中读取图像的方式,包括解码后端和相关参数。 23 | 2. **加载标注**:定义读取图像对应的标注信息的方式,包括是否包含边界框等。 24 | 3. **数据增强**:定义各种数据增强操作,如随机HSV增强、Mosaic增强、随机尺寸调整、随机裁剪、随机翻转、填充和MixUp增强等。 25 | 4. **数据打包**:将处理后的图像和标注信息打包成模型输入格式。 26 | 27 | 以 RTMDet 为例,我们将在下面的示例中展示如何配置数据加载器。 28 | 29 | ### 加载图像 30 | 31 | 加载图像模块用于从文件中读取图像。 32 | 33 | ```python 34 | dict( 35 | type=LoadImageFromFile, # 加载图像的类型 36 | imdecode_backend="pillow", # 解码后端 37 | backend_args=None, # 后端参数 38 | ) 39 | ``` 40 | 41 | ### 加载标注 42 | 43 | 加载标注模块用于读取图像对应的标注信息。 44 | 45 | ```python 46 | dict( 47 | type=LoadAnnotations, # 加载标注的类型 48 | imdecode_backend="pillow", # 解码后端 49 | with_bbox=True, # 是否包含边界框 50 | ) 51 | ``` 52 | 53 | ### 数据增强 54 | 55 | 数据增强模块用于对图像进行各种增强操作,以提高模型的泛化能力。 56 | 57 | ```python 58 | dict(type=HSVRandomAug), # 随机HSV增强 59 | 60 | dict( 61 | type=Mosaic, # Mosaic增强 62 | img_scale=imgsz, # 图像尺寸 63 | pad_val=114.0, # 填充值 64 | ), 65 | 66 | dict( 67 | type=RandomResize, # 随机尺寸调整 68 | scale=(imgsz[0] * 2, imgsz[1] * 2), # 尺寸范围 69 | ratio_range=(0.1, 2.0), # 比例范围 70 | resize_type=Resize, # 调整类型 71 | keep_ratio=True, # 是否保持比例 72 | ), 73 | 74 | dict( 75 | type=RandomCrop, # 随机裁剪 76 | crop_size=imgsz, # 裁剪尺寸 77 | ), 78 | 79 | dict( 80 | type=RandomFlip, # 随机翻转 81 | prob=0.5, # 翻转概率 82 | ), 83 | 84 | dict( 85 | type=Pad, # 填充 86 | size=imgsz, # 填充尺寸 87 | pad_val=dict(img=(114, 114, 114)), # 填充值 88 | ), 89 | 90 | dict( 91 | type=MixUp, # MixUp增强 92 | img_scale=imgsz, # 图像尺寸 93 | ratio_range=(1.0, 1.0), # 比例范围 94 | max_cached_images=20, # 最大缓存图像数量 95 | pad_val=114.0, # 填充值 96 | ), 97 | ``` 98 | 99 | #### 数据打包 100 | 101 | 数据打包模块用于将处理后的图像和标注信息打包成模型输入格式。 102 | 103 | ```python 104 | dict(type=PackDetInputs), # 数据打包 105 | ``` 106 | -------------------------------------------------------------------------------- /docs/zh_cn/datasets/images/labelme_pfld_1p.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Studio/ModelAssistant/aa7f20eaea560cd8c290074a331625d6a9e92de3/docs/zh_cn/datasets/images/labelme_pfld_1p.png -------------------------------------------------------------------------------- /docs/zh_cn/datasets/images/roboflow_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Studio/ModelAssistant/aa7f20eaea560cd8c290074a331625d6a9e92de3/docs/zh_cn/datasets/images/roboflow_download.png -------------------------------------------------------------------------------- /docs/zh_cn/datasets/images/roboflow_fomo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Studio/ModelAssistant/aa7f20eaea560cd8c290074a331625d6a9e92de3/docs/zh_cn/datasets/images/roboflow_fomo.png -------------------------------------------------------------------------------- /docs/zh_cn/datasets/images/roboflow_generate_version.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Studio/ModelAssistant/aa7f20eaea560cd8c290074a331625d6a9e92de3/docs/zh_cn/datasets/images/roboflow_generate_version.png -------------------------------------------------------------------------------- /docs/zh_cn/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | # https://vitepress.dev/reference/default-theme-home-page 3 | layout: home 4 | 5 | hero: 6 | name: SSCMA 7 | text: 专注于嵌入式人工智能的开源项目。 8 | tagline: 我们针对实际场景优化了出色的算法,并提供更用户友好的实现,从而在嵌入式设备上实现更快速、更准确的推断。 9 | image: 10 | src: https://files.seeedstudio.com/sscma/docs/images/SSCMA-Hero.png 11 | alt: SSCMA 12 | actions: 13 | - theme: brand 14 | text: 快速入门 15 | link: ./introduction/quick_start 16 | - theme: alt 17 | text: 安装 SSCMA 18 | link: ./introduction/installation 19 | - theme: alt 20 | text: 在 GitHub 上查看 21 | link: https://github.com/Seeed-Studio/ModelAssistant 22 | 23 | features: 24 | - icon: 🔍 25 | title: 异常检测 26 | details: 在现实世界中,异常数据通常很难识别,即使能够识别,也需要很高的成本。异常检测算法以低成本的方式收集正常数据,将正常数据之外的任何内容视为异常。 27 | - icon: 👁️ 28 | title: 计算机视觉 29 | details: 我们提供了许多计算机视觉算法,如目标检测、图像分类、图像分割和姿态估计。我们优化了这些计算机视觉算法,在低端设备上实现了良好的运行速度和准确性。 30 | - icon: ⏱️ 31 | title: 特定场景 32 | details: SSCMA 为特定的生产环境提供定制化场景,例如模拟仪器的识别、传统数字仪表和音频分类。我们将继续在未来添加更多针对特定场景的算法。 33 | --- 34 | -------------------------------------------------------------------------------- /docs/zh_cn/tutorials/deploy/grove_vision_ai_v2.md: -------------------------------------------------------------------------------- 1 | # 在 Grove Vision AI V2 上部署 SSCMA 模型 2 | 3 | 本示例为 [SSCMA](https://github.com/Seeed-Studio/ModelAssistant) 包含的模型在 Grove Vision AI V2 模块的部署教程, 4 | 5 | 在 Grove Vision AI V2 上部署 SSCMA 模型,您需要先将模型转换为量化的 TensorFlow Lite 格式,Vela 转换器转换后,您可以将模型部署到 Grove Vision AI V2 模块上。SSCMA 在导出工具中添加了这个功能,您可以在导出过程中,添加参数以 `--format vela` 来导出 Vela 模型。成功导出后,剩下的步骤就和 [SSCMA - 模型部署](overview)中一致了。 6 | 7 | 此外,您也可以尝试手动构建固件来适配模型。 8 | 9 | ## 在 Linux 环境下构建固件 10 | 11 | 以下步骤已在 Ubuntu 20.04 PC 上测试通过。 12 | 13 | ### 安装依赖 14 | 15 | ```bash 16 | sudo apt install make 17 | ``` 18 | 19 | ### 下载 Arm GNU 工具链 20 | 21 | ```bash 22 | cd ~ 23 | wget https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-eabi.tar.xz 24 | ``` 25 | 26 | ### 解压文件 27 | 28 | ```bash 29 | tar -xvf arm-gnu-toolchain-13.2.rel1-x86_64-arm-none-eabi.tar.xz 30 | ``` 31 | 32 | ### 添加到 PATH 33 | 34 | ```bash 35 | export PATH="$HOME/arm-gnu-toolchain-13.2.Rel1-x86_64-arm-none-eabi/bin/:$PATH" 36 | ``` 37 | 38 | ### 克隆以下仓库并进入 Seeed_Grove_Vision_AI_Module_V2 文件夹 39 | 40 | ```bash 41 | git clone --recursive https://github.com/HimaxWiseEyePlus/Seeed_Grove_Vision_AI_Module_V2.git 42 | cd Seeed_Grove_Vision_AI_Module_V2 43 | ``` 44 | 45 | ### 编译固件 46 | 47 | ```bash 48 | cd EPII_CM55M_APP_S 49 | make clean 50 | make 51 | ``` 52 | 53 | 输出的 ELF 文件位于 `/obj_epii_evb_icv30_bdv10/gnu_epii_evb_WLCSP65/EPII_CM55M_gnu_epii_evb_WLCSP65_s.elf`。 54 | 55 | ### 生成固件镜像文件 56 | 57 | ```bash 58 | cd ../we2_image_gen_local/ 59 | cp ../EPII_CM55M_APP_S/obj_epii_evb_icv30_bdv10/gnu_epii_evb_WLCSP65/EPII_CM55M_gnu_epii_evb_WLCSP65_s.elf input_case1_secboot/ 60 | ./we2_local_image_gen project_case1_blp_wlcsp.json 61 | ``` 62 | 63 | 输出的固件镜像位于 `./output_case1_sec_wlcsp/output.img`。 64 | 65 | ### 刷入固件 66 | 67 | 您可以使用 SSCMA Web Toolkit 或者 Grove Vision AI V2 的 USB 转串口工具刷入固件,也可以直接使用 Xmodem 协议刷入固件。 68 | -------------------------------------------------------------------------------- /docs/zh_cn/tutorials/deploy/images/sscma-upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Seeed-Studio/ModelAssistant/aa7f20eaea560cd8c290074a331625d6a9e92de3/docs/zh_cn/tutorials/deploy/images/sscma-upload.png -------------------------------------------------------------------------------- /docs/zh_cn/tutorials/overview.md: -------------------------------------------------------------------------------- 1 | # 流程概览 2 | 3 | 使用确定算法解决实际问题的步骤并不简单,SSCMA 通常将其划分为以下几个步骤,您可以参考以下流程图来了解整个流程。 4 | 5 | ```mermaid 6 | graph TD 7 | A[确定目标和需求] --> B[选择合适的算法] --> C[训练、导出和部署模型] --> D[解决实际问题] 8 | ``` 9 | 10 | 目前 SSCMA 提供了多种算法,您可以根据自己的需求选择合适的算法,然后通过训练、导出和部署模型来解决实际问题。本章将进一步介绍如何使用 SSCMA 来训练、导出和部署模型。对与常见的有监督学习任务,使用 SSCMA 的流程如下图所示: 11 | 12 | ```mermaid 13 | graph TD 14 | A(开始) --> B(数据收集、处理) 15 | B --> C(数据标注) 16 | C --> D(模型选择、训练) 17 | D --> E{模型验证、导出} 18 | E -- 否 --> B 19 | E -- 否 --> K{QAT} 20 | K --> E 21 | E -- 是 --> G(部署、维护) 22 | G --> I(结束) 23 | ``` 24 | 25 | 26 | ## 数据收集、处理 27 | 28 | 收集相关数据,并进行清洗、格式化和转换,以便于后续的分析和建模。 29 | 30 | :::tip 31 | 32 | - 确保数据的质量和相关性。 33 | - 处理缺失值、异常值和重复数据。 34 | - 进行特征工程,提取有用的信息。 35 | - 关于数据集的收集和处理方法,请参考 [SSCMA - 公共数据集](../datasets/public)。 36 | 37 | ::: 38 | 39 | 40 | ## 数据标注 41 | 42 | 对数据进行标注,为机器学习模型提供训练所需的标签。 43 | 44 | :::tip 45 | 46 | - 确保标注的准确性和一致性。 47 | - 考虑使用众包或专业的标注团队。 48 | - 定义清晰的标注指南。 49 | - 关于数据集标注方法,请参考 [SSCMA - 自制数据集](../datasets/custom) 和 [SSCMA - 数据集格式与拓展](../datasets/extension)。 50 | 51 | ::: 52 | 53 | ## 模型选择、训练 54 | 55 | 选择合适的机器学习模型,并使用训练数据集对模型进行训练。 56 | 57 | :::tip 58 | 59 | - 根据问题类型(分类、回归等)选择合适的模型。 60 | - 调整模型参数以优化性能。 61 | - 使用交叉验证等方法评估模型的泛化能力。 62 | 63 | ::: 64 | 65 | ### Epochs 与 Batch Size 66 | 67 | 简单来说 Epochs 指的是将整个训练数据集完整地通过神经网络一次,即模型对所有训练样本学习一次。在训练过程中,模型通过多个 Epoch 来不断优化参数,以减少误差,提高模型的准确率,一般情况下,Epoch 数越多,模型完全收敛的概率越高,但也会增加训练时间。在 SSCMA 中,您可以通过 `max_epochs` 参数来设置训练的 Epoch 数。 68 | 69 | Batch Size 是指在训练过程中每次迭代更新权重时所使用的样本数量,会影响训练速度和模型的性能,一般来说,其设置需要根据具体的问题和数据集来确定。 70 | 71 | ### 优化器与学习率 72 | 73 | 优化器是一种算法,用于在训练过程中调整模型的参数,以最小化损失函数,常见的优化器有 SGD、Adam、AdamW 等。优化器的选择对模型的训练速度和性能有重要影响,对于大多数情况,AdamW 优化器是一个不错的选择,您可以通过 `optim_wrapper` 参数来设置特定的优化器。 74 | 75 | 学习率是优化器在更新模型参数时的步长。它决定了在每次迭代中,参数沿着梯度下降方向移动的距离。学习率的选择对模型的训练速度和最终性能有重要影响,对于不同的算法和数据集,可能需要不同的学习率,您可以通过 `base_lr` 参数来设置学习率。我们建议您根据训练的损失曲线来调整学习率。 76 | 77 | - 学习率过大:可能导致训练过程不稳定,甚至发散。 78 | - 学习率过小:训练过程会非常缓慢,可能陷入局部最小值。 79 | 80 | :::tip 81 | 82 | 关于训练细节的更多信息,请参考 [SSCMA - 自定义 - 训练与验证管线](../custom/pipelines) 和 [SSCMA - 自定义 - 优化器](../custom/optimizer)。 83 | 84 | ::: 85 | 86 | 87 | ## 模型验证、导出 88 | 89 | 使用验证和测试数据集评估模型的性能,确保模型在未见过的数据上也能表现良好。在嵌入式设备上部署模型之前,您需要将模型导出为适合目标设备的格式,此过程可能包含量化、剪枝、图优化等操作,以减小模型的体积和计算量,这些操作可能会影响模型的性能,因此对于导出后的模型,我们建议您在部署前进行验证。 90 | 91 | :::tip 92 | 93 | - 避免过拟合,即模型在训练数据上表现很好,但在新数据上表现不佳。 94 | - 如果模型表现不佳,可能需要返回到数据预处理或模型选择阶段进行调整。 95 | 96 | ::: 97 | 98 | ## QAT 99 | 100 | 如果在步骤[模型验证、导出](#模型验证、导出)中发现导出的量化模型性能不佳,您可以考虑使用 SSCMA 导出工具的 QAT(量化感知训练)功能,以提高模型精度,对于经过 QAT 导出的模型,您需要重新进行验证。 101 | 102 | ## 部署、维护 103 | 104 | 将训练好的模型部署到生产环境中,使其能够处理实际的数据并提供预测。 105 | 106 | :::tip 107 | 108 | - 确保模型的部署环境与训练环境一致。 109 | - 考虑模型的可扩展性和维护性。 110 | - 定期检查模型的性能,并根据需要进行更新。 111 | 112 | ::: 113 | -------------------------------------------------------------------------------- /mmengine/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | # flake8: noqa 3 | from .config import * 4 | from .fileio import * 5 | from .logging import * 6 | from .registry import * 7 | from .utils import * 8 | from .version import __version__, version_info 9 | -------------------------------------------------------------------------------- /mmengine/_strategy/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from mmengine.utils import digit_version 3 | from mmengine.utils.dl_utils import TORCH_VERSION 4 | from .base import BaseStrategy 5 | from .colossalai import ColossalAIStrategy 6 | from .deepspeed import DeepSpeedStrategy 7 | from .distributed import DDPStrategy 8 | from .single_device import SingleDeviceStrategy 9 | 10 | __all__ = [ 11 | 'BaseStrategy', 'DDPStrategy', 'SingleDeviceStrategy', 'DeepSpeedStrategy', 12 | 'ColossalAIStrategy' 13 | ] 14 | 15 | if digit_version(TORCH_VERSION) >= digit_version('2.0.0'): 16 | try: 17 | from .fsdp import FSDPStrategy # noqa:F401 18 | __all__.append('FSDPStrategy') 19 | except: # noqa: E722 20 | pass 21 | -------------------------------------------------------------------------------- /mmengine/_strategy/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from torch._subclasses.fake_tensor import _is_tensor_constructor 3 | from torch.utils._python_dispatch import TorchDispatchMode 4 | 5 | 6 | class MetaTensorContext(TorchDispatchMode): 7 | 8 | def __torch_dispatch__(self, func, types, args=(), kwargs=None): 9 | if _is_tensor_constructor(func): 10 | device_idx = [arg.name 11 | for arg in func._schema.arguments].index('device') 12 | if len(args) > device_idx: 13 | args = list(args) 14 | args[device_idx] = 'meta' 15 | else: 16 | kwargs['device'] = 'meta' 17 | return func(*args, **kwargs) 18 | -------------------------------------------------------------------------------- /mmengine/analysis/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .complexity_analysis import (ActivationAnalyzer, FlopAnalyzer, 3 | activation_count, flop_count, 4 | parameter_count, parameter_count_table) 5 | from .print_helper import get_model_complexity_info 6 | 7 | __all__ = [ 8 | 'FlopAnalyzer', 'ActivationAnalyzer', 'flop_count', 'activation_count', 9 | 'parameter_count', 'parameter_count_table', 'get_model_complexity_info' 10 | ] 11 | -------------------------------------------------------------------------------- /mmengine/config/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .config import Config, ConfigDict, DictAction, read_base, ModifyConstant 3 | 4 | __all__ = ['Config', 'ConfigDict', 'DictAction', 'read_base', "ModifyConstant"] 5 | -------------------------------------------------------------------------------- /mmengine/dataset/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .base_dataset import BaseDataset, Compose, force_full_init 3 | from .dataset_wrapper import ClassBalancedDataset, ConcatDataset, RepeatDataset 4 | from .sampler import DefaultSampler, InfiniteSampler 5 | from .utils import (COLLATE_FUNCTIONS, default_collate, pseudo_collate, 6 | worker_init_fn) 7 | 8 | __all__ = [ 9 | 'BaseDataset', 'Compose', 'force_full_init', 'ClassBalancedDataset', 10 | 'ConcatDataset', 'RepeatDataset', 'DefaultSampler', 'InfiniteSampler', 11 | 'worker_init_fn', 'pseudo_collate', 'COLLATE_FUNCTIONS', 'default_collate' 12 | ] 13 | -------------------------------------------------------------------------------- /mmengine/device/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .utils import (get_device, get_max_cuda_memory, get_max_musa_memory, 3 | is_cuda_available, is_dipu_available, is_mlu_available, 4 | is_mps_available, is_musa_available, is_npu_available, 5 | is_npu_support_full_precision) 6 | 7 | __all__ = [ 8 | 'get_max_cuda_memory', 'get_device', 'is_cuda_available', 9 | 'is_mlu_available', 'is_mps_available', 'is_npu_available', 10 | 'is_dipu_available', 'get_max_musa_memory', 'is_musa_available', 11 | 'is_npu_support_full_precision' 12 | ] 13 | -------------------------------------------------------------------------------- /mmengine/dist/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .dist import (all_gather_object, all_reduce, all_gather, all_reduce_dict, 3 | collect_results, gather, broadcast, gather_object, 4 | sync_random_seed, broadcast_object_list, 5 | collect_results_cpu, collect_results_gpu, all_reduce_params) 6 | from .utils import (get_dist_info, init_dist, init_local_group, get_backend, 7 | get_world_size, get_rank, get_local_size, get_local_rank, 8 | is_main_process, master_only, barrier, get_local_group, 9 | is_distributed, get_default_group, get_data_device, 10 | get_comm_device, cast_data_device, infer_launcher) 11 | 12 | __all__ = [ 13 | 'all_gather_object', 'all_reduce', 'all_gather', 'all_reduce_dict', 14 | 'collect_results', 'collect_results_cpu', 'collect_results_gpu', 'gather', 15 | 'broadcast', 'gather_object', 'sync_random_seed', 'broadcast_object_list', 16 | 'get_dist_info', 'init_dist', 'init_local_group', 'get_backend', 17 | 'get_world_size', 'get_rank', 'get_local_size', 'get_local_group', 18 | 'get_local_rank', 'is_main_process', 'master_only', 'barrier', 19 | 'is_distributed', 'get_default_group', 'all_reduce_params', 20 | 'get_data_device', 'get_comm_device', 'cast_data_device', 'infer_launcher' 21 | ] 22 | -------------------------------------------------------------------------------- /mmengine/evaluator/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .evaluator import Evaluator 3 | from .metric import BaseMetric, DumpResults 4 | from .utils import get_metric_value 5 | 6 | __all__ = ['BaseMetric', 'Evaluator', 'get_metric_value', 'DumpResults'] 7 | -------------------------------------------------------------------------------- /mmengine/evaluator/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from typing import Any, Dict 3 | 4 | 5 | def get_metric_value(indicator: str, metrics: Dict) -> Any: 6 | """Get the metric value specified by an indicator, which can be either a 7 | metric name or a full name with evaluator prefix. 8 | 9 | Args: 10 | indicator (str): The metric indicator, which can be the metric name 11 | (e.g. 'AP') or the full name with prefix (e.g. 'COCO/AP') 12 | metrics (dict): The evaluation results output by the evaluator 13 | 14 | Returns: 15 | Any: The specified metric value 16 | """ 17 | 18 | if '/' in indicator: 19 | # The indicator is a full name 20 | if indicator in metrics: 21 | return metrics[indicator] 22 | else: 23 | raise ValueError( 24 | f'The indicator "{indicator}" can not match any metric in ' 25 | f'{list(metrics.keys())}') 26 | else: 27 | # The indicator is metric name without prefix 28 | matched = [k for k in metrics.keys() if k.split('/')[-1] == indicator] 29 | 30 | if not matched: 31 | raise ValueError( 32 | f'The indicator {indicator} can not match any metric in ' 33 | f'{list(metrics.keys())}') 34 | elif len(matched) > 1: 35 | raise ValueError(f'The indicator "{indicator}" matches multiple ' 36 | f'metrics {matched}') 37 | else: 38 | return metrics[matched[0]] 39 | -------------------------------------------------------------------------------- /mmengine/fileio/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .backends import (BaseStorageBackend, HTTPBackend, LmdbBackend, 3 | LocalBackend, MemcachedBackend, PetrelBackend, 4 | register_backend) 5 | from .file_client import FileClient, HardDiskBackend 6 | from .handlers import (BaseFileHandler, JsonHandler, PickleHandler, 7 | YamlHandler, register_handler) 8 | from .io import (copy_if_symlink_fails, copyfile, copyfile_from_local, 9 | copyfile_to_local, copytree, copytree_from_local, 10 | copytree_to_local, dump, exists, generate_presigned_url, get, 11 | get_file_backend, get_local_path, get_text, isdir, isfile, 12 | join_path, list_dir_or_file, load, put, put_text, remove, 13 | rmtree) 14 | from .parse import dict_from_file, list_from_file 15 | 16 | __all__ = [ 17 | 'BaseStorageBackend', 'FileClient', 'PetrelBackend', 'MemcachedBackend', 18 | 'LmdbBackend', 'HardDiskBackend', 'LocalBackend', 'HTTPBackend', 19 | 'copy_if_symlink_fails', 'copyfile', 'copyfile_from_local', 20 | 'copyfile_to_local', 'copytree', 'copytree_from_local', 21 | 'copytree_to_local', 'exists', 'generate_presigned_url', 'get', 22 | 'get_file_backend', 'get_local_path', 'get_text', 'isdir', 'isfile', 23 | 'join_path', 'list_dir_or_file', 'put', 'put_text', 'remove', 'rmtree', 24 | 'load', 'dump', 'register_handler', 'BaseFileHandler', 'JsonHandler', 25 | 'PickleHandler', 'YamlHandler', 'list_from_file', 'dict_from_file', 26 | 'register_backend' 27 | ] 28 | -------------------------------------------------------------------------------- /mmengine/fileio/backends/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .base import BaseStorageBackend 3 | from .http_backend import HTTPBackend 4 | from .lmdb_backend import LmdbBackend 5 | from .local_backend import LocalBackend 6 | from .memcached_backend import MemcachedBackend 7 | from .petrel_backend import PetrelBackend 8 | from .registry_utils import backends, prefix_to_backends, register_backend 9 | 10 | __all__ = [ 11 | 'BaseStorageBackend', 'LocalBackend', 'HTTPBackend', 'LmdbBackend', 12 | 'MemcachedBackend', 'PetrelBackend', 'register_backend', 'backends', 13 | 'prefix_to_backends' 14 | ] 15 | -------------------------------------------------------------------------------- /mmengine/fileio/backends/base.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import logging 3 | from abc import ABCMeta, abstractmethod 4 | 5 | from mmengine.logging import print_log 6 | 7 | 8 | class BaseStorageBackend(metaclass=ABCMeta): 9 | """Abstract class of storage backends. 10 | 11 | All backends need to implement two apis: :meth:`get()` and 12 | :meth:`get_text()`. 13 | 14 | - :meth:`get()` reads the file as a byte stream. 15 | - :meth:`get_text()` reads the file as texts. 16 | """ 17 | 18 | # a flag to indicate whether the backend can create a symlink for a file 19 | # This attribute will be deprecated in future. 20 | _allow_symlink = False 21 | 22 | @property 23 | def allow_symlink(self): 24 | print_log( 25 | 'allow_symlink will be deprecated in future', 26 | logger='current', 27 | level=logging.WARNING) 28 | return self._allow_symlink 29 | 30 | @property 31 | def name(self): 32 | return self.__class__.__name__ 33 | 34 | @abstractmethod 35 | def get(self, filepath): 36 | pass 37 | 38 | @abstractmethod 39 | def get_text(self, filepath): 40 | pass 41 | -------------------------------------------------------------------------------- /mmengine/fileio/backends/http_backend.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import os 3 | import tempfile 4 | from contextlib import contextmanager 5 | from pathlib import Path 6 | from typing import Generator, Union 7 | from urllib.request import urlopen 8 | 9 | from .base import BaseStorageBackend 10 | 11 | 12 | class HTTPBackend(BaseStorageBackend): 13 | """HTTP and HTTPS storage bachend.""" 14 | 15 | def get(self, filepath: str) -> bytes: 16 | """Read bytes from a given ``filepath``. 17 | 18 | Args: 19 | filepath (str): Path to read data. 20 | 21 | Returns: 22 | bytes: Expected bytes object. 23 | 24 | Examples: 25 | >>> backend = HTTPBackend() 26 | >>> backend.get('http://path/of/file') 27 | b'hello world' 28 | """ 29 | return urlopen(filepath).read() 30 | 31 | def get_text(self, filepath, encoding='utf-8') -> str: 32 | """Read text from a given ``filepath``. 33 | 34 | Args: 35 | filepath (str): Path to read data. 36 | encoding (str): The encoding format used to open the ``filepath``. 37 | Defaults to 'utf-8'. 38 | 39 | Returns: 40 | str: Expected text reading from ``filepath``. 41 | 42 | Examples: 43 | >>> backend = HTTPBackend() 44 | >>> backend.get_text('http://path/of/file') 45 | 'hello world' 46 | """ 47 | return urlopen(filepath).read().decode(encoding) 48 | 49 | @contextmanager 50 | def get_local_path( 51 | self, filepath: str) -> Generator[Union[str, Path], None, None]: 52 | """Download a file from ``filepath`` to a local temporary directory, 53 | and return the temporary path. 54 | 55 | ``get_local_path`` is decorated by :meth:`contxtlib.contextmanager`. It 56 | can be called with ``with`` statement, and when exists from the 57 | ``with`` statement, the temporary path will be released. 58 | 59 | Args: 60 | filepath (str): Download a file from ``filepath``. 61 | 62 | Yields: 63 | Iterable[str]: Only yield one temporary path. 64 | 65 | Examples: 66 | >>> backend = HTTPBackend() 67 | >>> # After existing from the ``with`` clause, 68 | >>> # the path will be removed 69 | >>> with backend.get_local_path('http://path/of/file') as path: 70 | ... # do something here 71 | """ 72 | try: 73 | f = tempfile.NamedTemporaryFile(delete=False) 74 | f.write(self.get(filepath)) 75 | f.close() 76 | yield f.name 77 | finally: 78 | os.remove(f.name) 79 | -------------------------------------------------------------------------------- /mmengine/fileio/backends/lmdb_backend.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from pathlib import Path 3 | from typing import Union 4 | 5 | from .base import BaseStorageBackend 6 | 7 | 8 | class LmdbBackend(BaseStorageBackend): 9 | """Lmdb storage backend. 10 | 11 | Args: 12 | db_path (str): Lmdb database path. 13 | readonly (bool): Lmdb environment parameter. If True, disallow any 14 | write operations. Defaults to True. 15 | lock (bool): Lmdb environment parameter. If False, when concurrent 16 | access occurs, do not lock the database. Defaults to False. 17 | readahead (bool): Lmdb environment parameter. If False, disable the OS 18 | filesystem readahead mechanism, which may improve random read 19 | performance when a database is larger than RAM. Defaults to False. 20 | **kwargs: Keyword arguments passed to `lmdb.open`. 21 | 22 | Attributes: 23 | db_path (str): Lmdb database path. 24 | """ 25 | 26 | def __init__(self, 27 | db_path, 28 | readonly=True, 29 | lock=False, 30 | readahead=False, 31 | **kwargs): 32 | try: 33 | import lmdb # noqa: F401 34 | except ImportError: 35 | raise ImportError( 36 | 'Please run "pip install lmdb" to enable LmdbBackend.') 37 | 38 | self.db_path = str(db_path) 39 | self.readonly = readonly 40 | self.lock = lock 41 | self.readahead = readahead 42 | self.kwargs = kwargs 43 | self._client = None 44 | 45 | def get(self, filepath: Union[str, Path]) -> bytes: 46 | """Get values according to the filepath. 47 | 48 | Args: 49 | filepath (str or Path): Here, filepath is the lmdb key. 50 | 51 | Returns: 52 | bytes: Expected bytes object. 53 | 54 | Examples: 55 | >>> backend = LmdbBackend('path/to/lmdb') 56 | >>> backend.get('key') 57 | b'hello world' 58 | """ 59 | if self._client is None: 60 | self._client = self._get_client() 61 | 62 | filepath = str(filepath) 63 | with self._client.begin(write=False) as txn: 64 | value_buf = txn.get(filepath.encode('ascii')) 65 | return value_buf 66 | 67 | def get_text(self, filepath, encoding=None): 68 | raise NotImplementedError 69 | 70 | def _get_client(self): 71 | import lmdb 72 | 73 | return lmdb.open( 74 | self.db_path, 75 | readonly=self.readonly, 76 | lock=self.lock, 77 | readahead=self.readahead, 78 | **self.kwargs) 79 | 80 | def __del__(self): 81 | if self._client is not None: 82 | self._client.close() 83 | -------------------------------------------------------------------------------- /mmengine/fileio/backends/memcached_backend.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from pathlib import Path 3 | from typing import Union 4 | 5 | from .base import BaseStorageBackend 6 | 7 | 8 | class MemcachedBackend(BaseStorageBackend): 9 | """Memcached storage backend. 10 | 11 | Attributes: 12 | server_list_cfg (str): Config file for memcached server list. 13 | client_cfg (str): Config file for memcached client. 14 | sys_path (str, optional): Additional path to be appended to `sys.path`. 15 | Defaults to None. 16 | """ 17 | 18 | def __init__(self, server_list_cfg, client_cfg, sys_path=None): 19 | if sys_path is not None: 20 | import sys 21 | sys.path.append(sys_path) 22 | try: 23 | import mc 24 | except ImportError: 25 | raise ImportError( 26 | 'Please install memcached to enable MemcachedBackend.') 27 | 28 | self.server_list_cfg = server_list_cfg 29 | self.client_cfg = client_cfg 30 | self._client = mc.MemcachedClient.GetInstance(self.server_list_cfg, 31 | self.client_cfg) 32 | # mc.pyvector servers as a point which points to a memory cache 33 | self._mc_buffer = mc.pyvector() 34 | 35 | def get(self, filepath: Union[str, Path]): 36 | """Get values according to the filepath. 37 | 38 | Args: 39 | filepath (str or Path): Path to read data. 40 | 41 | Returns: 42 | bytes: Expected bytes object. 43 | 44 | Examples: 45 | >>> server_list_cfg = '/path/of/server_list.conf' 46 | >>> client_cfg = '/path/of/mc.conf' 47 | >>> backend = MemcachedBackend(server_list_cfg, client_cfg) 48 | >>> backend.get('/path/of/file') 49 | b'hello world' 50 | """ 51 | filepath = str(filepath) 52 | import mc 53 | self._client.Get(filepath, self._mc_buffer) 54 | value_buf = mc.ConvertBuffer(self._mc_buffer) 55 | return value_buf 56 | 57 | def get_text(self, filepath, encoding=None): 58 | raise NotImplementedError 59 | -------------------------------------------------------------------------------- /mmengine/fileio/handlers/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .base import BaseFileHandler 3 | from .json_handler import JsonHandler 4 | from .pickle_handler import PickleHandler 5 | from .registry_utils import file_handlers, register_handler 6 | from .yaml_handler import YamlHandler 7 | 8 | __all__ = [ 9 | 'BaseFileHandler', 'JsonHandler', 'PickleHandler', 'YamlHandler', 10 | 'register_handler', 'file_handlers' 11 | ] 12 | -------------------------------------------------------------------------------- /mmengine/fileio/handlers/base.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from abc import ABCMeta, abstractmethod 3 | 4 | 5 | class BaseFileHandler(metaclass=ABCMeta): 6 | # `str_like` is a flag to indicate whether the type of file object is 7 | # str-like object or bytes-like object. Pickle only processes bytes-like 8 | # objects but json only processes str-like object. If it is str-like 9 | # object, `StringIO` will be used to process the buffer. 10 | str_like = True 11 | 12 | @abstractmethod 13 | def load_from_fileobj(self, file, **kwargs): 14 | pass 15 | 16 | @abstractmethod 17 | def dump_to_fileobj(self, obj, file, **kwargs): 18 | pass 19 | 20 | @abstractmethod 21 | def dump_to_str(self, obj, **kwargs): 22 | pass 23 | 24 | def load_from_path(self, filepath, mode='r', **kwargs): 25 | with open(filepath, mode) as f: 26 | return self.load_from_fileobj(f, **kwargs) 27 | 28 | def dump_to_path(self, obj, filepath, mode='w', **kwargs): 29 | with open(filepath, mode) as f: 30 | self.dump_to_fileobj(obj, f, **kwargs) 31 | -------------------------------------------------------------------------------- /mmengine/fileio/handlers/json_handler.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import json 3 | 4 | import numpy as np 5 | 6 | from .base import BaseFileHandler 7 | 8 | 9 | def set_default(obj): 10 | """Set default json values for non-serializable values. 11 | 12 | It helps convert ``set``, ``range`` and ``np.ndarray`` data types to list. 13 | It also converts ``np.generic`` (including ``np.int32``, ``np.float32``, 14 | etc.) into plain numbers of plain python built-in types. 15 | """ 16 | if isinstance(obj, (set, range)): 17 | return list(obj) 18 | elif isinstance(obj, np.ndarray): 19 | return obj.tolist() 20 | elif isinstance(obj, np.generic): 21 | return obj.item() 22 | raise TypeError(f'{type(obj)} is unsupported for json dump') 23 | 24 | 25 | class JsonHandler(BaseFileHandler): 26 | 27 | def load_from_fileobj(self, file): 28 | return json.load(file) 29 | 30 | def dump_to_fileobj(self, obj, file, **kwargs): 31 | kwargs.setdefault('default', set_default) 32 | json.dump(obj, file, **kwargs) 33 | 34 | def dump_to_str(self, obj, **kwargs): 35 | kwargs.setdefault('default', set_default) 36 | return json.dumps(obj, **kwargs) 37 | -------------------------------------------------------------------------------- /mmengine/fileio/handlers/pickle_handler.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import pickle 3 | 4 | from .base import BaseFileHandler 5 | 6 | 7 | class PickleHandler(BaseFileHandler): 8 | 9 | str_like = False 10 | 11 | def load_from_fileobj(self, file, **kwargs): 12 | return pickle.load(file, **kwargs) 13 | 14 | def load_from_path(self, filepath, **kwargs): 15 | return super().load_from_path(filepath, mode='rb', **kwargs) 16 | 17 | def dump_to_str(self, obj, **kwargs): 18 | kwargs.setdefault('protocol', 2) 19 | return pickle.dumps(obj, **kwargs) 20 | 21 | def dump_to_fileobj(self, obj, file, **kwargs): 22 | kwargs.setdefault('protocol', 2) 23 | pickle.dump(obj, file, **kwargs) 24 | 25 | def dump_to_path(self, obj, filepath, **kwargs): 26 | super().dump_to_path(obj, filepath, mode='wb', **kwargs) 27 | -------------------------------------------------------------------------------- /mmengine/fileio/handlers/registry_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from mmengine.utils import is_list_of 3 | from .base import BaseFileHandler 4 | from .json_handler import JsonHandler 5 | from .pickle_handler import PickleHandler 6 | from .yaml_handler import YamlHandler 7 | 8 | file_handlers = { 9 | 'json': JsonHandler(), 10 | 'yaml': YamlHandler(), 11 | 'yml': YamlHandler(), 12 | 'pickle': PickleHandler(), 13 | 'pkl': PickleHandler(), 14 | } 15 | 16 | 17 | def _register_handler(handler, file_formats): 18 | """Register a handler for some file extensions. 19 | 20 | Args: 21 | handler (:obj:`BaseFileHandler`): Handler to be registered. 22 | file_formats (str or list[str]): File formats to be handled by this 23 | handler. 24 | """ 25 | if not isinstance(handler, BaseFileHandler): 26 | raise TypeError( 27 | f'handler must be a child of BaseFileHandler, not {type(handler)}') 28 | if isinstance(file_formats, str): 29 | file_formats = [file_formats] 30 | if not is_list_of(file_formats, str): 31 | raise TypeError('file_formats must be a str or a list of str') 32 | for ext in file_formats: 33 | file_handlers[ext] = handler 34 | 35 | 36 | def register_handler(file_formats, **kwargs): 37 | 38 | def wrap(cls): 39 | _register_handler(cls(**kwargs), file_formats) 40 | return cls 41 | 42 | return wrap 43 | -------------------------------------------------------------------------------- /mmengine/fileio/handlers/yaml_handler.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import yaml 3 | 4 | try: 5 | from yaml import CDumper as Dumper # type: ignore 6 | from yaml import CLoader as Loader # type: ignore 7 | except ImportError: 8 | from yaml import Loader, Dumper # type: ignore 9 | 10 | from .base import BaseFileHandler # isort:skip 11 | 12 | 13 | class YamlHandler(BaseFileHandler): 14 | 15 | def load_from_fileobj(self, file, **kwargs): 16 | kwargs.setdefault('Loader', Loader) 17 | return yaml.load(file, **kwargs) 18 | 19 | def dump_to_fileobj(self, obj, file, **kwargs): 20 | kwargs.setdefault('Dumper', Dumper) 21 | yaml.dump(obj, file, **kwargs) 22 | 23 | def dump_to_str(self, obj, **kwargs): 24 | kwargs.setdefault('Dumper', Dumper) 25 | return yaml.dump(obj, **kwargs) 26 | -------------------------------------------------------------------------------- /mmengine/hooks/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .checkpoint_hook import CheckpointHook 3 | from .early_stopping_hook import EarlyStoppingHook 4 | from .ema_hook import EMAHook 5 | from .empty_cache_hook import EmptyCacheHook 6 | from .hook import Hook 7 | from .iter_timer_hook import IterTimerHook 8 | from .logger_hook import LoggerHook 9 | from .naive_visualization_hook import NaiveVisualizationHook 10 | from .param_scheduler_hook import ParamSchedulerHook 11 | from .profiler_hook import NPUProfilerHook, ProfilerHook 12 | from .runtime_info_hook import RuntimeInfoHook 13 | from .sampler_seed_hook import DistSamplerSeedHook 14 | from .sync_buffer_hook import SyncBuffersHook 15 | from .test_time_aug_hook import PrepareTTAHook 16 | from .visualization_hook import VisualizationHook 17 | 18 | __all__ = [ 19 | "Hook", 20 | "IterTimerHook", 21 | "DistSamplerSeedHook", 22 | "ParamSchedulerHook", 23 | "SyncBuffersHook", 24 | "EmptyCacheHook", 25 | "CheckpointHook", 26 | "LoggerHook", 27 | "NaiveVisualizationHook", 28 | "EMAHook", 29 | "RuntimeInfoHook", 30 | "ProfilerHook", 31 | "PrepareTTAHook", 32 | "NPUProfilerHook", 33 | "EarlyStoppingHook", 34 | "VisualizationHook", 35 | ] 36 | -------------------------------------------------------------------------------- /mmengine/hooks/sampler_seed_hook.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from mmengine.registry import HOOKS 3 | from .hook import Hook 4 | 5 | 6 | @HOOKS.register_module() 7 | class DistSamplerSeedHook(Hook): 8 | """Data-loading sampler for distributed training. 9 | 10 | When distributed training, it is only useful in conjunction with 11 | :obj:`EpochBasedRunner`, while :obj:`IterBasedRunner` achieves the same 12 | purpose with :obj:`IterLoader`. 13 | """ 14 | 15 | priority = 'NORMAL' 16 | 17 | def before_train_epoch(self, runner) -> None: 18 | """Set the seed for sampler and batch_sampler. 19 | 20 | Args: 21 | runner (Runner): The runner of the training process. 22 | """ 23 | if hasattr(runner.train_loop.dataloader, 'sampler') and hasattr( 24 | runner.train_loop.dataloader.sampler, 'set_epoch'): 25 | # In case the` _SingleProcessDataLoaderIter` has no sampler, 26 | # or data loader uses `SequentialSampler` in Pytorch. 27 | runner.train_loop.dataloader.sampler.set_epoch(runner.epoch) 28 | 29 | elif hasattr(runner.train_loop.dataloader, 30 | 'batch_sampler') and hasattr( 31 | runner.train_loop.dataloader.batch_sampler.sampler, 32 | 'set_epoch'): 33 | # In case the` _SingleProcessDataLoaderIter` has no batch sampler. 34 | # batch sampler in pytorch warps the sampler as its attributes. 35 | runner.train_loop.dataloader.batch_sampler.sampler.set_epoch( 36 | runner.epoch) 37 | -------------------------------------------------------------------------------- /mmengine/hooks/sync_buffer_hook.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from mmengine.dist import all_reduce_params, is_distributed 3 | from mmengine.registry import HOOKS 4 | from .hook import Hook 5 | 6 | 7 | @HOOKS.register_module() 8 | class SyncBuffersHook(Hook): 9 | """Synchronize model buffers such as running_mean and running_var in BN at 10 | the end of each epoch.""" 11 | 12 | priority = 'NORMAL' 13 | 14 | def __init__(self) -> None: 15 | self.distributed = is_distributed() 16 | # A flag to mark whether synchronization has been done in 17 | # after_train_epoch 18 | self.called_in_train = False 19 | 20 | def before_val_epoch(self, runner) -> None: 21 | """All-reduce model buffers before each validation epoch. 22 | 23 | Synchronize the buffers before each validation if they have not been 24 | synchronized at the end of the previous training epoch. This method 25 | will be called when using IterBasedTrainLoop. 26 | 27 | Args: 28 | runner (Runner): The runner of the training process. 29 | """ 30 | if self.distributed: 31 | if not self.called_in_train: 32 | all_reduce_params(runner.model.buffers(), op='mean') 33 | self.called_in_train = False 34 | 35 | def after_train_epoch(self, runner) -> None: 36 | """All-reduce model buffers at the end of each epoch. 37 | 38 | Args: 39 | runner (Runner): The runner of the training process. 40 | """ 41 | if self.distributed: 42 | all_reduce_params(runner.model.buffers(), op='mean') 43 | self.called_in_train = True 44 | -------------------------------------------------------------------------------- /mmengine/hooks/test_time_aug_hook.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from typing import TYPE_CHECKING 3 | 4 | if TYPE_CHECKING: 5 | from mmengine.runner import Runner 6 | 7 | from mmengine.hooks import Hook 8 | from mmengine.registry import HOOKS, MODELS, RUNNERS 9 | 10 | 11 | @HOOKS.register_module() 12 | class PrepareTTAHook(Hook): 13 | """Wraps `runner.model` with subclass of :class:`BaseTTAModel` in 14 | `before_test`. 15 | 16 | Note: 17 | This function will only be used with :obj:`MMFullyShardedDataParallel`. 18 | 19 | Args: 20 | tta_cfg (dict): Config dictionary of the test time augmentation model. 21 | """ 22 | 23 | def __init__(self, tta_cfg: dict): 24 | self.tta_cfg = tta_cfg 25 | 26 | def before_test(self, runner: 'Runner') -> None: 27 | """Wraps `runner.model` with the subclass of :class:`BaseTTAModel`. 28 | 29 | Args: 30 | runner (Runner): The runner of the testing process. 31 | """ 32 | self.tta_cfg['module'] = runner.model # type: ignore 33 | model = MODELS.build(self.tta_cfg) 34 | runner.model = model # type: ignore 35 | 36 | 37 | def build_runner_with_tta(cfg: dict) -> 'Runner': 38 | """Builds runner with tta (test time augmentation) transformation and 39 | TTAModel. 40 | 41 | Note: 42 | This function will only be used with :obj:`MMFullyShardedDataParallel`. 43 | 44 | Args: 45 | cfg (dict): cfg with ``tta_pipeline`` and ``tta_model`` 46 | 47 | Notes: 48 | This is only an experimental feature. We may refactor the code in the 49 | future. 50 | 51 | Returns: 52 | Runner: Runner with tta transformation and TTAModel 53 | """ 54 | assert hasattr( 55 | cfg, 56 | 'tta_model'), ('please make sure tta_model is defined in your config.') 57 | assert hasattr(cfg, 'tta_pipeline'), ( 58 | 'please make sure tta_pipeline is defined in your config.') 59 | cfg['test_dataloader']['dataset']['pipeline'] = cfg['tta_pipeline'] 60 | 61 | if 'runner_type' in cfg: 62 | runner = RUNNERS.build(cfg) 63 | else: 64 | from mmengine.runner import Runner 65 | runner = Runner.from_cfg(cfg) 66 | 67 | runner.register_hook(PrepareTTAHook(tta_cfg=cfg['tta_model'])) 68 | return runner 69 | -------------------------------------------------------------------------------- /mmengine/hub/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .hub import get_config, get_model 3 | 4 | __all__ = ['get_config', 'get_model'] 5 | -------------------------------------------------------------------------------- /mmengine/hub/deprecated.json: -------------------------------------------------------------------------------- 1 | { 2 | "resnet50_caffe": "detectron/resnet50_caffe", 3 | "resnet50_caffe_bgr": "detectron2/resnet50_caffe_bgr", 4 | "resnet101_caffe": "detectron/resnet101_caffe", 5 | "resnet101_caffe_bgr": "detectron2/resnet101_caffe_bgr" 6 | } 7 | -------------------------------------------------------------------------------- /mmengine/infer/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .infer import BaseInferencer 3 | 4 | __all__ = ['BaseInferencer'] 5 | -------------------------------------------------------------------------------- /mmengine/logging/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .history_buffer import HistoryBuffer 3 | from .logger import MMLogger, print_log 4 | from .message_hub import MessageHub 5 | 6 | __all__ = ['HistoryBuffer', 'MessageHub', 'MMLogger', 'print_log'] 7 | -------------------------------------------------------------------------------- /mmengine/model/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from mmengine.utils.dl_utils import TORCH_VERSION 3 | from mmengine.utils.version_utils import digit_version 4 | from .averaged_model import (BaseAveragedModel, ExponentialMovingAverage, 5 | MomentumAnnealingEMA, StochasticWeightAverage) 6 | from .base_model import BaseDataPreprocessor, BaseModel, ImgDataPreprocessor 7 | from .base_module import BaseModule, ModuleDict, ModuleList, Sequential 8 | from .test_time_aug import BaseTTAModel 9 | from .utils import (convert_sync_batchnorm, detect_anomalous_params, 10 | merge_dict, revert_sync_batchnorm, stack_batch) 11 | from .weight_init import (BaseInit, Caffe2XavierInit, ConstantInit, 12 | KaimingInit, NormalInit, PretrainedInit, 13 | TruncNormalInit, UniformInit, XavierInit, 14 | bias_init_with_prob, caffe2_xavier_init, 15 | constant_init, initialize, kaiming_init, normal_init, 16 | trunc_normal_init, uniform_init, update_init_info, 17 | xavier_init) 18 | from .wrappers import (MMDistributedDataParallel, 19 | MMSeparateDistributedDataParallel, is_model_wrapper) 20 | from .plugin import build_plugin_layer,infer_abbr 21 | 22 | __all__ = [ 23 | 'MMDistributedDataParallel', 'is_model_wrapper', 'BaseAveragedModel', 24 | 'StochasticWeightAverage', 'ExponentialMovingAverage', 25 | 'MomentumAnnealingEMA', 'BaseModel', 'BaseDataPreprocessor', 26 | 'ImgDataPreprocessor', 'MMSeparateDistributedDataParallel', 'BaseModule', 27 | 'stack_batch', 'merge_dict', 'detect_anomalous_params', 'ModuleList', 28 | 'ModuleDict', 'Sequential', 'revert_sync_batchnorm', 'update_init_info', 29 | 'constant_init', 'xavier_init', 'normal_init', 'trunc_normal_init', 30 | 'uniform_init', 'kaiming_init', 'caffe2_xavier_init', 31 | 'bias_init_with_prob', 'BaseInit', 'ConstantInit', 'XavierInit', 32 | 'NormalInit', 'TruncNormalInit', 'UniformInit', 'KaimingInit', 33 | 'Caffe2XavierInit', 'PretrainedInit', 'initialize', 34 | 'convert_sync_batchnorm', 'BaseTTAModel','build_plugin_layer','infer_abbr' 35 | ] 36 | 37 | if digit_version(TORCH_VERSION) >= digit_version('2.0.0'): 38 | from .wrappers import MMFullyShardedDataParallel # noqa:F401 39 | __all__.append('MMFullyShardedDataParallel') 40 | -------------------------------------------------------------------------------- /mmengine/model/base_model/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .base_model import BaseModel 3 | from .data_preprocessor import BaseDataPreprocessor, ImgDataPreprocessor 4 | 5 | __all__ = ['BaseModel', 'ImgDataPreprocessor', 'BaseDataPreprocessor'] 6 | -------------------------------------------------------------------------------- /mmengine/model/wrappers/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from mmengine.utils.dl_utils import TORCH_VERSION 3 | from mmengine.utils.version_utils import digit_version 4 | from .distributed import MMDistributedDataParallel 5 | from .seperate_distributed import MMSeparateDistributedDataParallel 6 | from .utils import is_model_wrapper 7 | 8 | __all__ = [ 9 | 'MMDistributedDataParallel', 'is_model_wrapper', 10 | 'MMSeparateDistributedDataParallel' 11 | ] 12 | 13 | if digit_version(TORCH_VERSION) >= digit_version('2.0.0'): 14 | from .fully_sharded_distributed import \ 15 | MMFullyShardedDataParallel # noqa:F401 16 | __all__.append('MMFullyShardedDataParallel') 17 | -------------------------------------------------------------------------------- /mmengine/model/wrappers/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import torch.nn as nn 3 | 4 | from mmengine.registry import MODEL_WRAPPERS, Registry 5 | 6 | 7 | def is_model_wrapper(model: nn.Module, registry: Registry = MODEL_WRAPPERS): 8 | """Check if a module is a model wrapper. 9 | 10 | The following 4 model in MMEngine (and their subclasses) are regarded as 11 | model wrappers: DataParallel, DistributedDataParallel, 12 | MMDataParallel, MMDistributedDataParallel. You may add you own 13 | model wrapper by registering it to ``mmengine.registry.MODEL_WRAPPERS``. 14 | 15 | Args: 16 | model (nn.Module): The model to be checked. 17 | registry (Registry): The parent registry to search for model wrappers. 18 | 19 | Returns: 20 | bool: True if the input model is a model wrapper. 21 | """ 22 | module_wrappers = tuple(registry.module_dict.values()) 23 | if isinstance(model, module_wrappers): 24 | return True 25 | 26 | if not registry.children: 27 | return False 28 | 29 | return any( 30 | is_model_wrapper(model, child) for child in registry.children.values()) 31 | -------------------------------------------------------------------------------- /mmengine/optim/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .optimizer import (OPTIM_WRAPPER_CONSTRUCTORS, OPTIMIZERS, 3 | AmpOptimWrapper, ApexOptimWrapper, BaseOptimWrapper, 4 | DefaultOptimWrapperConstructor, OptimWrapper, 5 | OptimWrapperDict, ZeroRedundancyOptimizer, 6 | build_optim_wrapper) 7 | # yapf: disable 8 | from .scheduler import (ConstantLR, ConstantMomentum, ConstantParamScheduler, 9 | CosineAnnealingLR, CosineAnnealingMomentum, 10 | CosineAnnealingParamScheduler, ExponentialLR, 11 | ExponentialMomentum, ExponentialParamScheduler, 12 | LinearLR, LinearMomentum, LinearParamScheduler, 13 | MultiStepLR, MultiStepMomentum, 14 | MultiStepParamScheduler, OneCycleLR, 15 | OneCycleParamScheduler, PolyLR, PolyMomentum, 16 | PolyParamScheduler, ReduceOnPlateauLR, 17 | ReduceOnPlateauMomentum, ReduceOnPlateauParamScheduler, 18 | StepLR, StepMomentum, StepParamScheduler, 19 | _ParamScheduler) 20 | 21 | # yapf: enable 22 | __all__ = [ 23 | 'OPTIM_WRAPPER_CONSTRUCTORS', 'OPTIMIZERS', 'build_optim_wrapper', 24 | 'DefaultOptimWrapperConstructor', 'ConstantLR', 'CosineAnnealingLR', 25 | 'ExponentialLR', 'LinearLR', 'MultiStepLR', 'StepLR', 'ConstantMomentum', 26 | 'CosineAnnealingMomentum', 'ExponentialMomentum', 'LinearMomentum', 27 | 'MultiStepMomentum', 'StepMomentum', 'ConstantParamScheduler', 28 | 'CosineAnnealingParamScheduler', 'ExponentialParamScheduler', 29 | 'LinearParamScheduler', 'MultiStepParamScheduler', 'StepParamScheduler', 30 | '_ParamScheduler', 'OptimWrapper', 'AmpOptimWrapper', 'ApexOptimWrapper', 31 | 'OptimWrapperDict', 'OneCycleParamScheduler', 'OneCycleLR', 'PolyLR', 32 | 'PolyMomentum', 'PolyParamScheduler', 'ReduceOnPlateauLR', 33 | 'ReduceOnPlateauMomentum', 'ReduceOnPlateauParamScheduler', 34 | 'ZeroRedundancyOptimizer', 'BaseOptimWrapper' 35 | ] 36 | -------------------------------------------------------------------------------- /mmengine/optim/optimizer/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .amp_optimizer_wrapper import AmpOptimWrapper 3 | from .apex_optimizer_wrapper import ApexOptimWrapper 4 | from .base import BaseOptimWrapper 5 | from .builder import (OPTIM_WRAPPER_CONSTRUCTORS, OPTIMIZERS, 6 | build_optim_wrapper) 7 | from .default_constructor import DefaultOptimWrapperConstructor 8 | from .optimizer_wrapper import OptimWrapper 9 | from .optimizer_wrapper_dict import OptimWrapperDict 10 | from .zero_optimizer import ZeroRedundancyOptimizer 11 | 12 | __all__ = [ 13 | 'OPTIM_WRAPPER_CONSTRUCTORS', 'OPTIMIZERS', 14 | 'DefaultOptimWrapperConstructor', 'build_optim_wrapper', 'OptimWrapper', 15 | 'AmpOptimWrapper', 'ApexOptimWrapper', 'OptimWrapperDict', 16 | 'ZeroRedundancyOptimizer', 'BaseOptimWrapper' 17 | ] 18 | -------------------------------------------------------------------------------- /mmengine/optim/scheduler/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | # yapf: disable 3 | from .lr_scheduler import (ConstantLR, CosineAnnealingLR, CosineRestartLR, 4 | ExponentialLR, LinearLR, MultiStepLR, OneCycleLR, 5 | PolyLR, ReduceOnPlateauLR, StepLR) 6 | from .momentum_scheduler import (ConstantMomentum, CosineAnnealingMomentum, 7 | CosineRestartMomentum, ExponentialMomentum, 8 | LinearMomentum, MultiStepMomentum, 9 | PolyMomentum, ReduceOnPlateauMomentum, 10 | StepMomentum) 11 | from .param_scheduler import (ConstantParamScheduler, 12 | CosineAnnealingParamScheduler, 13 | CosineRestartParamScheduler, 14 | ExponentialParamScheduler, LinearParamScheduler, 15 | MultiStepParamScheduler, OneCycleParamScheduler, 16 | PolyParamScheduler, 17 | ReduceOnPlateauParamScheduler, 18 | StepParamScheduler, _ParamScheduler) 19 | 20 | # yapf: enable 21 | __all__ = [ 22 | 'ConstantLR', 'CosineAnnealingLR', 'ExponentialLR', 'LinearLR', 23 | 'MultiStepLR', 'StepLR', 'ConstantMomentum', 'CosineAnnealingMomentum', 24 | 'ExponentialMomentum', 'LinearMomentum', 'MultiStepMomentum', 25 | 'StepMomentum', 'ConstantParamScheduler', 'CosineAnnealingParamScheduler', 26 | 'ExponentialParamScheduler', 'LinearParamScheduler', 27 | 'MultiStepParamScheduler', 'StepParamScheduler', '_ParamScheduler', 28 | 'PolyParamScheduler', 'PolyLR', 'PolyMomentum', 'OneCycleParamScheduler', 29 | 'OneCycleLR', 'CosineRestartParamScheduler', 'CosineRestartLR', 30 | 'CosineRestartMomentum', 'ReduceOnPlateauParamScheduler', 31 | 'ReduceOnPlateauLR', 'ReduceOnPlateauMomentum' 32 | ] 33 | -------------------------------------------------------------------------------- /mmengine/registry/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .build_functions import (build_from_cfg, build_model_from_cfg, 3 | build_runner_from_cfg, build_scheduler_from_cfg) 4 | from .default_scope import DefaultScope 5 | from .registry import Registry 6 | from .root import (DATA_SAMPLERS, DATASETS, EVALUATOR, FUNCTIONS, HOOKS, 7 | INFERENCERS, LOG_PROCESSORS, LOOPS, METRICS, MODEL_WRAPPERS, 8 | MODELS, OPTIM_WRAPPER_CONSTRUCTORS, OPTIM_WRAPPERS, 9 | OPTIMIZERS, PARAM_SCHEDULERS, RUNNER_CONSTRUCTORS, RUNNERS, 10 | STRATEGIES, TASK_UTILS, TRANSFORMS, VISBACKENDS, 11 | VISUALIZERS, WEIGHT_INITIALIZERS) 12 | from .utils import (count_registered_modules, init_default_scope, 13 | traverse_registry_tree) 14 | 15 | __all__ = [ 16 | 'Registry', 'RUNNERS', 'RUNNER_CONSTRUCTORS', 'HOOKS', 'DATASETS', 17 | 'DATA_SAMPLERS', 'TRANSFORMS', 'MODELS', 'WEIGHT_INITIALIZERS', 18 | 'OPTIMIZERS', 'OPTIM_WRAPPER_CONSTRUCTORS', 'TASK_UTILS', 19 | 'PARAM_SCHEDULERS', 'METRICS', 'MODEL_WRAPPERS', 'OPTIM_WRAPPERS', 'LOOPS', 20 | 'VISBACKENDS', 'VISUALIZERS', 'LOG_PROCESSORS', 'EVALUATOR', 'INFERENCERS', 21 | 'DefaultScope', 'traverse_registry_tree', 'count_registered_modules', 22 | 'build_model_from_cfg', 'build_runner_from_cfg', 'build_from_cfg', 23 | 'build_scheduler_from_cfg', 'init_default_scope', 'FUNCTIONS', 'STRATEGIES' 24 | ] 25 | -------------------------------------------------------------------------------- /mmengine/registry/root.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | """MMEngine provides 20 root registries to support using modules across 3 | projects. 4 | 5 | More datails can be found at 6 | https://mmengine.readthedocs.io/en/latest/advanced_tutorials/registry.html. 7 | """ 8 | 9 | from .build_functions import (build_model_from_cfg, build_runner_from_cfg, 10 | build_scheduler_from_cfg) 11 | from .registry import Registry 12 | 13 | # manage all kinds of runners like `EpochBasedRunner` and `IterBasedRunner` 14 | RUNNERS = Registry('runner', build_func=build_runner_from_cfg) 15 | # manage runner constructors that define how to initialize runners 16 | RUNNER_CONSTRUCTORS = Registry('runner constructor') 17 | # manage all kinds of loops like `EpochBasedTrainLoop` 18 | LOOPS = Registry('loop') 19 | # manage all kinds of hooks like `CheckpointHook` 20 | HOOKS = Registry('hook') 21 | 22 | # manage all kinds of strategies like `NativeStrategy` and `DDPStrategy` 23 | STRATEGIES = Registry('strategy') 24 | 25 | # manage data-related modules 26 | DATASETS = Registry('dataset') 27 | DATA_SAMPLERS = Registry('data sampler') 28 | TRANSFORMS = Registry('transform') 29 | 30 | # mangage all kinds of modules inheriting `nn.Module` 31 | MODELS = Registry('model', build_model_from_cfg) 32 | # mangage all kinds of model wrappers like 'MMDistributedDataParallel' 33 | MODEL_WRAPPERS = Registry('model_wrapper') 34 | # mangage all kinds of weight initialization modules like `Uniform` 35 | WEIGHT_INITIALIZERS = Registry('weight initializer') 36 | 37 | # mangage all kinds of optimizers like `SGD` and `Adam` 38 | OPTIMIZERS = Registry('optimizer') 39 | # manage optimizer wrapper 40 | OPTIM_WRAPPERS = Registry('optim_wrapper') 41 | # manage constructors that customize the optimization hyperparameters. 42 | OPTIM_WRAPPER_CONSTRUCTORS = Registry('optimizer wrapper constructor') 43 | # mangage all kinds of parameter schedulers like `MultiStepLR` 44 | PARAM_SCHEDULERS = Registry( 45 | 'parameter scheduler', build_func=build_scheduler_from_cfg) 46 | 47 | # manage all kinds of metrics 48 | METRICS = Registry('metric') 49 | # manage evaluator 50 | EVALUATOR = Registry('evaluator') 51 | 52 | # manage task-specific modules like anchor generators and box coders 53 | TASK_UTILS = Registry('task util') 54 | 55 | # manage visualizer 56 | VISUALIZERS = Registry('visualizer') 57 | # manage visualizer backend 58 | VISBACKENDS = Registry('vis_backend') 59 | 60 | # manage logprocessor 61 | LOG_PROCESSORS = Registry('log_processor') 62 | 63 | # manage inferencer 64 | INFERENCERS = Registry('inferencer') 65 | 66 | # manage function 67 | FUNCTIONS = Registry('function') 68 | -------------------------------------------------------------------------------- /mmengine/runner/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from ._flexible_runner import FlexibleRunner 3 | from .activation_checkpointing import turn_on_activation_checkpointing 4 | from .amp import autocast 5 | from .base_loop import BaseLoop 6 | from .checkpoint import (CheckpointLoader, find_latest_checkpoint, 7 | get_deprecated_model_names, get_external_models, 8 | get_mmcls_models, get_state_dict, 9 | get_torchvision_models, load_checkpoint, 10 | load_state_dict, save_checkpoint, weights_to_cpu) 11 | from .log_processor import LogProcessor 12 | from .loops import EpochBasedTrainLoop, IterBasedTrainLoop, TestLoop, ValLoop 13 | from .priority import Priority, get_priority 14 | from .runner import Runner 15 | from .utils import set_random_seed 16 | 17 | __all__ = [ 18 | 'BaseLoop', 'load_state_dict', 'get_torchvision_models', 19 | 'get_external_models', 'get_mmcls_models', 'get_deprecated_model_names', 20 | 'CheckpointLoader', 'load_checkpoint', 'weights_to_cpu', 'get_state_dict', 21 | 'save_checkpoint', 'EpochBasedTrainLoop', 'IterBasedTrainLoop', 'ValLoop', 22 | 'TestLoop', 'Runner', 'get_priority', 'Priority', 'find_latest_checkpoint', 23 | 'autocast', 'LogProcessor', 'set_random_seed', 'FlexibleRunner', 24 | 'turn_on_activation_checkpointing' 25 | ] 26 | -------------------------------------------------------------------------------- /mmengine/runner/activation_checkpointing.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from functools import wraps 3 | from operator import attrgetter 4 | from typing import List, Union 5 | 6 | import torch 7 | from torch.utils.checkpoint import checkpoint 8 | 9 | 10 | def wrap_forward(forward): 11 | 12 | @wraps(forward) 13 | def wrapper(*args): 14 | return checkpoint(forward, *args) 15 | 16 | return wrapper 17 | 18 | 19 | def turn_on_activation_checkpointing(model: torch.nn.Module, 20 | modules: Union[List[str], str]): 21 | 22 | if isinstance(modules, str): 23 | modules = [modules] 24 | for module_name in modules: 25 | module = attrgetter(module_name)(model) 26 | module.forward = wrap_forward(module.forward) 27 | -------------------------------------------------------------------------------- /mmengine/runner/base_loop.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from abc import ABCMeta, abstractmethod 3 | from typing import Any, Dict, Union 4 | 5 | from torch.utils.data import DataLoader 6 | 7 | 8 | class BaseLoop(metaclass=ABCMeta): 9 | """Base loop class. 10 | 11 | All subclasses inherited from ``BaseLoop`` should overwrite the 12 | :meth:`run` method. 13 | 14 | Args: 15 | runner (Runner): A reference of runner. 16 | dataloader (Dataloader or dict): An iterator to generate one batch of 17 | dataset each iteration. 18 | """ 19 | 20 | def __init__(self, runner, dataloader: Union[DataLoader, Dict]) -> None: 21 | self._runner = runner 22 | if isinstance(dataloader, dict): 23 | # Determine whether or not different ranks use different seed. 24 | diff_rank_seed = runner._randomness_cfg.get( 25 | 'diff_rank_seed', False) 26 | self.dataloader = runner.build_dataloader( 27 | dataloader, seed=runner.seed, diff_rank_seed=diff_rank_seed) 28 | else: 29 | self.dataloader = dataloader 30 | 31 | @property 32 | def runner(self): 33 | return self._runner 34 | 35 | @abstractmethod 36 | def run(self) -> Any: 37 | """Execute loop.""" 38 | -------------------------------------------------------------------------------- /mmengine/runner/priority.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from enum import Enum 3 | from typing import Union 4 | 5 | 6 | class Priority(Enum): 7 | """Hook priority levels. 8 | 9 | +--------------+------------+ 10 | | Level | Value | 11 | +==============+============+ 12 | | HIGHEST | 0 | 13 | +--------------+------------+ 14 | | VERY_HIGH | 10 | 15 | +--------------+------------+ 16 | | HIGH | 30 | 17 | +--------------+------------+ 18 | | ABOVE_NORMAL | 40 | 19 | +--------------+------------+ 20 | | NORMAL | 50 | 21 | +--------------+------------+ 22 | | BELOW_NORMAL | 60 | 23 | +--------------+------------+ 24 | | LOW | 70 | 25 | +--------------+------------+ 26 | | VERY_LOW | 90 | 27 | +--------------+------------+ 28 | | LOWEST | 100 | 29 | +--------------+------------+ 30 | """ 31 | 32 | HIGHEST = 0 33 | VERY_HIGH = 10 34 | HIGH = 30 35 | ABOVE_NORMAL = 40 36 | NORMAL = 50 37 | BELOW_NORMAL = 60 38 | LOW = 70 39 | VERY_LOW = 90 40 | LOWEST = 100 41 | 42 | 43 | def get_priority(priority: Union[int, str, Priority]) -> int: 44 | """Get priority value. 45 | 46 | Args: 47 | priority (int or str or :obj:`Priority`): Priority. 48 | 49 | Returns: 50 | int: The priority value. 51 | """ 52 | if isinstance(priority, int): 53 | if priority < 0 or priority > 100: 54 | raise ValueError('priority must be between 0 and 100') 55 | return priority 56 | elif isinstance(priority, Priority): 57 | return priority.value 58 | elif isinstance(priority, str): 59 | return Priority[priority.upper()].value 60 | else: 61 | raise TypeError('priority must be an integer or Priority enum value') 62 | -------------------------------------------------------------------------------- /mmengine/structures/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .base_data_element import BaseDataElement 3 | from .instance_data import InstanceData 4 | from .label_data import LabelData 5 | from .pixel_data import PixelData 6 | 7 | __all__ = ['BaseDataElement', 'InstanceData', 'LabelData', 'PixelData'] 8 | -------------------------------------------------------------------------------- /mmengine/structures/label_data.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | 3 | import torch 4 | 5 | from .base_data_element import BaseDataElement 6 | 7 | 8 | class LabelData(BaseDataElement): 9 | """Data structure for label-level annotations or predictions.""" 10 | 11 | @staticmethod 12 | def onehot_to_label(onehot: torch.Tensor) -> torch.Tensor: 13 | """Convert the one-hot input to label. 14 | 15 | Args: 16 | onehot (torch.Tensor, optional): The one-hot input. The format 17 | of input must be one-hot. 18 | 19 | Returns: 20 | torch.Tensor: The converted results. 21 | """ 22 | assert isinstance(onehot, torch.Tensor) 23 | if (onehot.ndim == 1 and onehot.max().item() <= 1 24 | and onehot.min().item() >= 0): 25 | return onehot.nonzero().squeeze(-1) 26 | else: 27 | raise ValueError( 28 | 'input is not one-hot and can not convert to label') 29 | 30 | @staticmethod 31 | def label_to_onehot(label: torch.Tensor, num_classes: int) -> torch.Tensor: 32 | """Convert the label-format input to one-hot. 33 | 34 | Args: 35 | label (torch.Tensor): The label-format input. The format 36 | of item must be label-format. 37 | num_classes (int): The number of classes. 38 | 39 | Returns: 40 | torch.Tensor: The converted results. 41 | """ 42 | assert isinstance(label, torch.Tensor) 43 | onehot = label.new_zeros((num_classes, )) 44 | assert max(label, default=torch.tensor(0)).item() < num_classes 45 | onehot[label] = 1 46 | return onehot 47 | -------------------------------------------------------------------------------- /mmengine/testing/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .compare import (assert_allclose, assert_attrs_equal, 3 | assert_dict_contains_subset, assert_dict_has_keys, 4 | assert_is_norm_layer, assert_keys_equal, 5 | assert_params_all_zeros, check_python_script) 6 | from .runner_test_case import RunnerTestCase 7 | 8 | __all__ = [ 9 | 'assert_allclose', 'assert_dict_contains_subset', 'assert_keys_equal', 10 | 'assert_attrs_equal', 'assert_dict_has_keys', 'assert_is_norm_layer', 11 | 'assert_params_all_zeros', 'check_python_script', 'RunnerTestCase' 12 | ] 13 | -------------------------------------------------------------------------------- /mmengine/testing/_internal/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .distributed import MultiProcessTestCase 3 | 4 | __all__ = ['MultiProcessTestCase'] 5 | -------------------------------------------------------------------------------- /mmengine/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .manager import ManagerMeta, ManagerMixin 3 | from .misc import (apply_to, check_prerequisites, concat_list, 4 | deprecated_api_warning, deprecated_function, 5 | get_object_from_string, has_method, 6 | import_modules_from_strings, is_list_of, 7 | is_method_overridden, is_seq_of, is_str, is_tuple_of, 8 | iter_cast, list_cast, requires_executable, requires_package, 9 | slice_list, to_1tuple, to_2tuple, to_3tuple, to_4tuple, 10 | to_ntuple, tuple_cast) 11 | from .package_utils import (call_command, get_installed_path, install_package, 12 | is_installed) 13 | from .path import (check_file_exist, fopen, is_abs, is_filepath, 14 | mkdir_or_exist, scandir, symlink) 15 | from .progressbar import (ProgressBar, track_iter_progress, 16 | track_parallel_progress, track_progress) 17 | from .progressbar_rich import track_progress_rich 18 | from .timer import Timer, TimerError, check_time 19 | from .version_utils import digit_version, get_git_hash 20 | 21 | __all__ = [ 22 | 'is_str', 'iter_cast', 'list_cast', 'tuple_cast', 'is_seq_of', 23 | 'is_list_of', 'is_tuple_of', 'slice_list', 'concat_list', 24 | 'check_prerequisites', 'requires_package', 'requires_executable', 25 | 'is_filepath', 'fopen', 'check_file_exist', 'mkdir_or_exist', 'symlink', 26 | 'scandir', 'deprecated_api_warning', 'import_modules_from_strings', 27 | 'to_1tuple', 'to_2tuple', 'to_3tuple', 'to_4tuple', 'to_ntuple', 28 | 'is_installed', 'call_command', 'get_installed_path', 'install_package', 29 | 'is_abs', 'is_method_overridden', 'has_method', 'digit_version', 30 | 'get_git_hash', 'ManagerMeta', 'ManagerMixin', 'Timer', 'check_time', 31 | 'TimerError', 'ProgressBar', 'track_iter_progress', 32 | 'track_parallel_progress', 'track_progress', 'deprecated_function', 33 | 'apply_to', 'track_progress_rich', 'get_object_from_string' 34 | ] 35 | -------------------------------------------------------------------------------- /mmengine/utils/dl_utils/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | 3 | from .collect_env import collect_env 4 | from .hub import load_url 5 | from .misc import has_batch_norm, is_norm, mmcv_full_available, tensor2imgs 6 | from .parrots_wrapper import TORCH_VERSION 7 | from .setup_env import set_multi_processing 8 | from .time_counter import TimeCounter 9 | from .torch_ops import torch_meshgrid 10 | from .trace import is_jit_tracing 11 | 12 | __all__ = [ 13 | 'load_url', 'TORCH_VERSION', 'set_multi_processing', 'has_batch_norm', 14 | 'is_norm', 'tensor2imgs', 'mmcv_full_available', 'collect_env', 15 | 'torch_meshgrid', 'is_jit_tracing', 'TimeCounter' 16 | ] 17 | -------------------------------------------------------------------------------- /mmengine/utils/dl_utils/torch_ops.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import torch 3 | 4 | from ..version_utils import digit_version 5 | from .parrots_wrapper import TORCH_VERSION 6 | 7 | _torch_version_meshgrid_indexing = ( 8 | 'parrots' not in TORCH_VERSION 9 | and digit_version(TORCH_VERSION) >= digit_version('1.10.0a0')) 10 | 11 | 12 | def torch_meshgrid(*tensors): 13 | """A wrapper of torch.meshgrid to compat different PyTorch versions. 14 | 15 | Since PyTorch 1.10.0a0, torch.meshgrid supports the arguments ``indexing``. 16 | So we implement a wrapper here to avoid warning when using high-version 17 | PyTorch and avoid compatibility issues when using previous versions of 18 | PyTorch. 19 | 20 | Args: 21 | tensors (List[Tensor]): List of scalars or 1 dimensional tensors. 22 | 23 | Returns: 24 | Sequence[Tensor]: Sequence of meshgrid tensors. 25 | """ 26 | if _torch_version_meshgrid_indexing: 27 | return torch.meshgrid(*tensors, indexing='ij') 28 | else: 29 | return torch.meshgrid(*tensors) # Uses indexing='ij' by default 30 | -------------------------------------------------------------------------------- /mmengine/utils/dl_utils/trace.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import warnings 3 | 4 | import torch 5 | 6 | from ..version_utils import digit_version 7 | 8 | 9 | def is_jit_tracing() -> bool: 10 | if (torch.__version__ != 'parrots' 11 | and digit_version(torch.__version__) >= digit_version('1.6.0')): 12 | on_trace = torch.jit.is_tracing() 13 | # In PyTorch 1.6, torch.jit.is_tracing has a bug. 14 | # Refers to https://github.com/pytorch/pytorch/issues/42448 15 | if isinstance(on_trace, bool): 16 | return on_trace 17 | else: 18 | return torch._C._is_tracing() 19 | else: 20 | warnings.warn( 21 | 'torch.jit.is_tracing is only supported after v1.6.0. ' 22 | 'Therefore is_tracing returns False automatically. Please ' 23 | 'set on_trace manually if you are using trace.', UserWarning) 24 | return False 25 | -------------------------------------------------------------------------------- /mmengine/utils/dl_utils/visualize.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from unittest.mock import patch 3 | 4 | import torch 5 | import torch.nn as nn 6 | 7 | from mmengine.model import BaseModel 8 | from mmengine.registry import MODELS 9 | 10 | 11 | @MODELS.register_module() 12 | class ToyModel(BaseModel): 13 | 14 | def __init__(self, *args, **kwargs): 15 | super().__init__() 16 | self.conv = nn.Conv2d(1, 1, 1) 17 | 18 | def forward(self, *args, **kwargs): 19 | return {'loss': torch.tensor(0.0)} 20 | 21 | 22 | def update_params_step(self, loss): 23 | pass 24 | 25 | 26 | def runtimeinfo_step(self, runner, batch_idx, data_batch=None): 27 | runner.message_hub.update_info('iter', runner.iter) 28 | lr_dict = runner.optim_wrapper.get_lr() 29 | for name, lr in lr_dict.items(): 30 | runner.message_hub.update_scalar(f'train/{name}', lr[0]) 31 | 32 | momentum_dict = runner.optim_wrapper.get_momentum() 33 | for name, momentum in momentum_dict.items(): 34 | runner.message_hub.update_scalar(f'train/{name}', momentum[0]) 35 | 36 | 37 | @patch('mmengine.optim.optimizer.OptimWrapper.update_params', 38 | update_params_step) 39 | @patch('mmengine.hooks.RuntimeInfoHook.before_train_iter', runtimeinfo_step) 40 | def fake_run(cfg): 41 | from mmengine.runner import Runner 42 | cfg.pop('model') 43 | cfg.pop('visualizer') 44 | cfg.pop('val_dataloader') 45 | cfg.pop('val_evaluator') 46 | cfg.pop('val_cfg') 47 | cfg.pop('test_dataloader') 48 | cfg.pop('test_evaluator') 49 | cfg.pop('test_cfg') 50 | extra_cfg = dict( 51 | model=dict(type='ToyModel'), 52 | visualizer=dict( 53 | type='Visualizer', 54 | vis_backends=[ 55 | dict(type='TensorboardVisBackend', save_dir='temp_dir') 56 | ]), 57 | ) 58 | cfg.merge_from_dict(extra_cfg) 59 | # build the runner from config 60 | runner = Runner.from_cfg(cfg) 61 | 62 | # start training 63 | runner.train() 64 | -------------------------------------------------------------------------------- /mmengine/version.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | 3 | __version__ = '0.10.4' 4 | 5 | 6 | def parse_version_info(version_str): 7 | """Parse the version information. 8 | 9 | Args: 10 | version_str (str): version string like '0.1.0'. 11 | 12 | Returns: 13 | tuple: version information contains major, minor, micro version. 14 | """ 15 | version_info = [] 16 | for x in version_str.split('.'): 17 | if x.isdigit(): 18 | version_info.append(int(x)) 19 | elif x.find('rc') != -1: 20 | patch_version = x.split('rc') 21 | version_info.append(int(patch_version[0])) 22 | version_info.append(f'rc{patch_version[1]}') 23 | return tuple(version_info) 24 | 25 | 26 | version_info = parse_version_info(__version__) 27 | -------------------------------------------------------------------------------- /mmengine/visualization/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .vis_backend import (AimVisBackend, BaseVisBackend, ClearMLVisBackend, 3 | DVCLiveVisBackend, LocalVisBackend, MLflowVisBackend, 4 | NeptuneVisBackend, TensorboardVisBackend, 5 | WandbVisBackend) 6 | from .visualizer import Visualizer 7 | 8 | __all__ = [ 9 | 'Visualizer', 'BaseVisBackend', 'LocalVisBackend', 'WandbVisBackend', 10 | 'TensorboardVisBackend', 'MLflowVisBackend', 'ClearMLVisBackend', 11 | 'NeptuneVisBackend', 'DVCLiveVisBackend', 'AimVisBackend' 12 | ] 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "devDependencies": { 4 | "vitepress": "latest" 5 | }, 6 | "scripts": { 7 | "docs:dev": "vitepress dev docs --host 0.0.0.0", 8 | "docs:build": "vitepress build docs", 9 | "docs:preview": "vitepress preview docs" 10 | }, 11 | "dependencies": { 12 | "vitepress-plugin-mermaid": "^2.0.17" 13 | } 14 | } -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | addict 2 | termcolor 3 | torch-tb-profiler 4 | terminaltables 5 | pycocotools 6 | lance 7 | shapely 8 | torch>=2.1.0 9 | torchvision>=0.16.0 10 | torchaudio>=2.1.0 11 | tensorboard 12 | opencv-python>=4.6.0 13 | numpy>=1.23.0,<2.0.0 14 | pillow>=7.1.2 15 | torchinfo 16 | yapf 17 | rich 18 | pyyaml 19 | tqdm 20 | pyarrow 21 | albumentations 22 | scikit-learn 23 | timm 24 | psutil 25 | regex 26 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [isort] 2 | line_length = 79 3 | multi_line_output = 0 4 | extra_standard_library = setuptools 5 | known_first_party = sscma 6 | known_third_party = pytest,yaml 7 | no_lines_before = STDLIB,LOCALFOLDER 8 | default_section = THIRDPARTY 9 | 10 | [yapf] 11 | BASED_ON_STYLE = pep8 12 | BLANK_LINE_BEFORE_NESTED_CLASS_OR_DEF = true 13 | SPLIT_BEFORE_EXPRESSION_AFTER_OPENING_PAREN = true 14 | 15 | [codespell] 16 | ignore-words-list = nd, ba, warmup 17 | -------------------------------------------------------------------------------- /sscma/__init__.py: -------------------------------------------------------------------------------- 1 | import mmengine 2 | from mmengine.utils import digit_version 3 | 4 | from .version import __version__, version_info 5 | 6 | from .visualization import * # noqa: F401,F403 7 | 8 | 9 | mmengine_minimum_version = "0.3.0" 10 | mmengine_maximum_version = "1.0.0" 11 | mmengine_version = digit_version(mmengine.__version__) 12 | 13 | assert mmengine_version >= digit_version( 14 | mmengine_minimum_version 15 | ) and mmengine_version < digit_version(mmengine_maximum_version), ( 16 | f"MMEngine=={mmengine.__version__} is used but incompatible. " 17 | f"Please install mmengine>={mmengine_minimum_version}, " 18 | f"<{mmengine_maximum_version}." 19 | ) 20 | 21 | __all__ = ["__version__", "version_info", "digit_version"] 22 | -------------------------------------------------------------------------------- /sscma/datasets/__init__.py: -------------------------------------------------------------------------------- 1 | from .base_dataset import BaseDataset 2 | from .base_det_dataset import BaseDetDataset 3 | from .categories import IMAGENET_CATEGORIES, IMAGENET100_CATEGORIES 4 | from .coco import coco_collate, CocoDataset, BatchShapePolicy 5 | from .custom import CustomDataset, find_folders, get_samples 6 | from .data_preprocessor import ( 7 | ClsDataPreprocessor, 8 | BatchSyncRandomResize, 9 | YOLOXBatchSyncRandomResize, 10 | RandomBatchAugment, 11 | DetDataPreprocessor, 12 | ) 13 | 14 | from .imagenet import ImageNet, ImageNet21k 15 | from .lancedb_datasets import LanceDataset 16 | from .meter import MeterData 17 | from .fomo import CustomFomoCocoDataset 18 | from .anomaly_dataset import Microphone_dataset, Signal_dataset 19 | 20 | 21 | __all__ = [ 22 | "BaseDetDataset", 23 | "IMAGENET_CATEGORIES", 24 | "IMAGENET100_CATEGORIES", 25 | "coco_collate", 26 | "CocoDataset", 27 | "BatchShapePolicy", 28 | "find_folders", 29 | "get_samples", 30 | "YOLOXBatchSyncRandomResize", 31 | "RandomBatchAugment", 32 | "DetDataPreprocessor", 33 | "BatchSyncRandomResize", 34 | "ImageNet", 35 | "ImageNet21k", 36 | "BaseDataset", 37 | "CustomDataset", 38 | "ClsDataPreprocessor", 39 | "LanceDataset", 40 | "MeterData", 41 | "CustomFomoCocoDataset", 42 | "Microphone_dataset", 43 | "Signal_dataset", 44 | ] 45 | -------------------------------------------------------------------------------- /sscma/datasets/anomaly_dataset.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import os.path as osp 3 | from typing import Any 4 | import glob 5 | 6 | import torch 7 | import numpy as np 8 | from torch.utils.data import Dataset 9 | import torchaudio.transforms as T 10 | 11 | 12 | def generate_Mel_DBtans(sample_rate=96000, n_fft=64, n_mels=32): 13 | mel_transform = T.MelSpectrogram(sample_rate, n_fft=n_fft, n_mels=n_mels) 14 | db_transform = T.AmplitudeToDB() 15 | return mel_transform, db_transform 16 | 17 | 18 | class Signal_dataset(Dataset): 19 | 20 | def __init__( 21 | self, 22 | data_root=None, 23 | ): 24 | self.data_root = data_root 25 | self.sample = self.get_all_sample() 26 | 27 | def get_all_sample(self): 28 | sample = glob.glob(osp.join(self.data_root, self.tag, "*.npy"), recursive=True) 29 | return sample 30 | 31 | def __getitem__(self, index: Any) -> Any: 32 | npy_path = self.sample[index] 33 | data = np.load(npy_path) 34 | data = (data[0], data[1], data[2]) 35 | return torch.from_numpy(data) 36 | 37 | def __len__(self): 38 | return len(self.sample) 39 | 40 | 41 | class Microphone_dataset(Dataset): 42 | def __init__(self, data_root=None, data_prefix=None): 43 | self.data_root = osp.join(data_root, data_prefix) 44 | self.mel_transform, self.db_transform = generate_Mel_DBtans() 45 | 46 | self.sample = self.get_all_sample() 47 | 48 | def get_all_sample(self): 49 | sample = glob.glob(osp.join(self.data_root, "*.npy"), recursive=True) 50 | return sample 51 | 52 | def __getitem__(self, index: Any) -> Any: 53 | npy_path = self.sample[index] 54 | data = np.load(npy_path) 55 | return torch.from_numpy(data) 56 | 57 | def __len__(self): 58 | return len(self.sample) 59 | -------------------------------------------------------------------------------- /sscma/datasets/api_wrappers/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .coco_api import COCO, COCOeval, COCOPanoptic 3 | from .cocoeval_mp import COCOevalMP 4 | 5 | __all__ = ["COCO", "COCOeval", "COCOPanoptic", "COCOevalMP"] 6 | -------------------------------------------------------------------------------- /sscma/datasets/pipelines/__init__.py: -------------------------------------------------------------------------------- 1 | from .composition import AlbCompose 2 | 3 | __all__ = ["AlbCompose"] 4 | -------------------------------------------------------------------------------- /sscma/datasets/pipelines/composition.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Seeed Technology Co.,Ltd. All rights reserved. 2 | from typing import Dict, Optional, Sequence, Union 3 | 4 | import albumentations as A 5 | from albumentations.core.bbox_utils import BboxParams 6 | from albumentations.core.composition import BaseCompose, BasicTransform 7 | from albumentations.core.keypoints_utils import KeypointParams 8 | 9 | from mmengine import TRANSFORMS 10 | 11 | 12 | class AlbCompose(A.Compose): 13 | """The packaging of the compose class of alb, the purpose is to parse the 14 | pipeline in the configuration file. 15 | 16 | Args: 17 | transforms(list):The packaging of the compose class of alb, the purpose is to 18 | parse the pipeline in the configuration file 19 | bbox_params (BboxParams): Parameters for bounding boxes transforms 20 | keypoint_params (KeypointParams): Parameters for keypoints transforms 21 | additional_targets (dict): Dict with keys - new target name, values - old 22 | target name. ex: {'image2': 'image'} 23 | p (float): probability of applying all list of transforms. Default: 1.0. 24 | """ 25 | 26 | def __init__( 27 | self, 28 | transforms: Sequence[Dict], 29 | bbox_params: Optional[Union[dict, "BboxParams"]] = None, 30 | keypoint_params: Optional[Union[dict, "KeypointParams"]] = None, 31 | additional_targets: Optional[Dict[str, str]] = None, 32 | p: float = 1, 33 | ): 34 | pose_trans = [] 35 | for transform in transforms: 36 | if isinstance(transform, dict): 37 | transform = TRANSFORMS.build(transform) 38 | pose_trans.append(transform) 39 | elif isinstance(transforms, (BaseCompose, BasicTransform)): 40 | pose_trans.append(transform) 41 | else: 42 | raise TypeError( 43 | "transform must be callable or a dict, but got" 44 | f" {type(transform)}" 45 | ) 46 | if isinstance(keypoint_params, str): 47 | keypoint_params = A.KeypointParams(keypoint_params) 48 | 49 | super().__init__( 50 | transforms=pose_trans, 51 | bbox_params=bbox_params, 52 | keypoint_params=keypoint_params, 53 | additional_targets=additional_targets, 54 | p=p, 55 | ) 56 | -------------------------------------------------------------------------------- /sscma/datasets/samplers/__init__.py: -------------------------------------------------------------------------------- 1 | from .batch_sampler import AspectRatioBatchSampler, MultiDataAspectRatioBatchSampler 2 | 3 | 4 | __all__ = ["AspectRatioBatchSampler", "MultiDataAspectRatioBatchSampler"] 5 | -------------------------------------------------------------------------------- /sscma/datasets/transforms/__init__.py: -------------------------------------------------------------------------------- 1 | from .basetransform import avoid_cache_randomness, BaseTransform 2 | from .processing import RandomResizedCrop, ResizeEdge, CenterCrop, RandomResize 3 | from .loading import LoadImageFromFile, LoadAnnotations 4 | from .transforms import ( 5 | RandomFlip, 6 | toTensor, 7 | Resize, 8 | RandomCrop, 9 | Pad, 10 | HSVRandomAug, 11 | BaseMixImageTransform, 12 | MixUp, 13 | Mosaic, 14 | Bbox2FomoMask, 15 | ) 16 | from .formatting import ( 17 | PackInputs, 18 | PackDetInputs, 19 | PackMultiTaskInputs, 20 | Transpose, 21 | NumpyToPIL, 22 | PILToNumpy, 23 | Collect, 24 | ) 25 | 26 | 27 | __all__ = [ 28 | "avoid_cache_randomness", 29 | "RandomResizedCrop", 30 | "ResizeEdge", 31 | "RandomFlip", 32 | "toTensor", 33 | "Resize", 34 | "RandomCrop", 35 | "Pad", 36 | "HSVRandomAug", 37 | "BaseMixImageTransform", 38 | "MixUp", 39 | "Mosaic", 40 | "CenterCrop", 41 | "RandomResize", 42 | "LoadImageFromFile", 43 | "LoadAnnotations", 44 | "PackInputs", 45 | "PackDetInputs", 46 | "PackMultiTaskInputs", 47 | "Transpose", 48 | "NumpyToPIL", 49 | "PILToNumpy", 50 | "Collect", 51 | "BaseTransform", 52 | "Bbox2FomoMask", 53 | ] 54 | -------------------------------------------------------------------------------- /sscma/datasets/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .sampler import LanceDistributedSampler 2 | from .parse_pose import parse_pose_metainfo 3 | from .download import check_file, download, download_file, is_link 4 | 5 | __all__ = [ 6 | "LanceDistributedSampler", 7 | "parse_pose_metainfo", 8 | "check_file", 9 | "download", 10 | "download_file", 11 | "is_link", 12 | ] 13 | -------------------------------------------------------------------------------- /sscma/datasets/utils/sampler.py: -------------------------------------------------------------------------------- 1 | from torch.utils.data import Sampler 2 | import numpy as np 3 | import math 4 | 5 | 6 | class LanceDistributedSampler(Sampler): 7 | """Distributed sampler for LanceDataset. 8 | 9 | Param: 10 | total_data: The total length of the sampled data set 11 | rank: The current sampler sequence number 12 | total_worker: The total number of samplers 13 | shuffle: Whether to disrupt the sampled index order 14 | """ 15 | 16 | def __init__(self, total_data, rank, total_worker, shuffle=True): 17 | self.rank = rank 18 | self.total_worker = total_worker 19 | self.total_data = total_data 20 | self.epoch = 0 21 | 22 | self.step_len = math.ceil(self.total_data / self.total_worker) 23 | 24 | start_idx = self.rank * self.step_len 25 | end_idx = min((self.rank + 1) * self.step_len, self.total_data) 26 | 27 | self.data_indices = list(range(start_idx, end_idx)) 28 | 29 | if shuffle: 30 | np.random.shuffle(self.data_indices) 31 | 32 | def __iter__(self): 33 | yield from self.data_indices 34 | 35 | def __len__(self): 36 | return len(self.data_indices) 37 | -------------------------------------------------------------------------------- /sscma/deploy/backend/__init__.py: -------------------------------------------------------------------------------- 1 | from .base_infer import BaseInfer 2 | from .onnxruntime_infer import OnnxInfer 3 | from .torchscript_infer import TorchScriptInfer 4 | from .saved_model_infer import SavedModelInfer 5 | from .tflite_infer import TFliteInfer 6 | from .hailo_infer import HailoInfer 7 | 8 | __all__ = [ 9 | "BaseInfer", 10 | "OnnxInfer", 11 | "TorchScriptInfer", 12 | "SavedModelInfer", 13 | "TFliteInfer", 14 | "HailoInfer", 15 | ] 16 | -------------------------------------------------------------------------------- /sscma/deploy/backend/base_infer.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | import torch 3 | 4 | 5 | class BaseInfer(metaclass=ABCMeta): 6 | """Base class for inference. 7 | 8 | Args: 9 | init_cfg (dict or ConfigDict, optional): the config to control the 10 | initialization. Defaults to None. 11 | """ 12 | 13 | def __init__( 14 | self, 15 | weights="sscma.pt", 16 | device=torch.device("cpu"), 17 | ): 18 | self.weights = weights 19 | self.device = device 20 | 21 | @abstractmethod 22 | def infer(self, input_data): 23 | """Forward function for inference. 24 | 25 | Args: 26 | input_data: The input data for the forward function. 27 | 28 | Returns: 29 | ForwardResults: The output of the forward function. 30 | """ 31 | pass 32 | 33 | @abstractmethod 34 | def load_weights(self): 35 | """Load weights for inference.""" 36 | pass 37 | -------------------------------------------------------------------------------- /sscma/deploy/backend/hailo_infer.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import numpy as np 3 | 4 | from .base_infer import BaseInfer 5 | 6 | class HailoInfer(BaseInfer): 7 | 8 | def __init__(self, weights="sscma.har", device="cpu", script=None): 9 | super().__init__() 10 | # self.runner = Client#Runner(har=weights) 11 | self.device = device 12 | if script is not None: 13 | self.runner.load_model_script(script) 14 | 15 | def infer(self, input_data): 16 | results = [] 17 | B, _, _, _ = input_data.shape 18 | # if isinstance(input_data, torch.Tensor): 19 | # input_data = input_data.detach().cpu().numpy() 20 | # with self.runner.infer_context(InferenceContext.SDK_NATIVE) as ctx: 21 | # input_data = input_data.transpose(0, 2, 3, 1) 22 | # res = self.runner.infer(ctx, input_data) 23 | # for result in zip(*[np.split(r, B) for r in res]): 24 | # results.append([s.transpose(0, 3, 1, 2) for s in result]) 25 | 26 | return results 27 | 28 | def load_weights(self): 29 | return super().load_weights() 30 | -------------------------------------------------------------------------------- /sscma/deploy/backend/onnxruntime_infer.py: -------------------------------------------------------------------------------- 1 | import onnxruntime 2 | import torch 3 | 4 | from .base_infer import BaseInfer 5 | 6 | 7 | class OnnxInfer(BaseInfer): 8 | def __init__(self, weights="sscma.onnx", device=torch.device("cpu")): 9 | super().__init__(weights=weights, device=device) 10 | self.output_names = None 11 | self.input_name = None 12 | self.sess = None 13 | 14 | def infer(self, input_data, split=True): 15 | results = [] 16 | if split: 17 | for data in input_data.split(1, 0): 18 | # check if input_data is Tensor 19 | if isinstance(data, torch.Tensor): 20 | data = data.numpy() 21 | results.append( 22 | self.sess.run(self.output_names, {self.input_name: data}) 23 | ) 24 | else: 25 | # check if input_data is Tensor 26 | if isinstance(input_data, torch.Tensor): 27 | input_data = input_data.numpy() 28 | results = self.sess.run(self.output_names, {self.input_name: input_data}) 29 | return results 30 | 31 | def load_weights(self): 32 | self.sess = onnxruntime.InferenceSession( 33 | self.weights, 34 | providers=[ 35 | "TensorrtExecutionProvider", 36 | "CUDAExecutionProvider", 37 | "CPUExecutionProvider", 38 | ], 39 | ) 40 | self.input_name = self.sess.get_inputs()[0].name 41 | self.output_names = [x.name for x in self.sess.get_outputs()] 42 | -------------------------------------------------------------------------------- /sscma/deploy/backend/saved_model_infer.py: -------------------------------------------------------------------------------- 1 | import os.path as osp 2 | 3 | import torch 4 | 5 | from .base_infer import BaseInfer 6 | 7 | from sscma.utils import lazy_import 8 | 9 | 10 | class SavedModelInfer(BaseInfer): 11 | @lazy_import("tensorflow","tf") 12 | def __init__(self, weights="sscma.pb", device=torch.device("cpu")): 13 | super().__init__(weights=weights, device=device) 14 | self.model = None 15 | 16 | def infer(self, input_data): 17 | results = [] 18 | for data in input_data.split(1, 0): 19 | # check if input_data is Tensor 20 | if isinstance(data, torch.Tensor): 21 | # Torch Tensor convert NCWH to NHWC 22 | data = data.permute(0, 2, 3, 1).numpy() 23 | inputs = {"inputs": tf.convert_to_tensor(data)} 24 | ordered_result = self.model(**inputs) 25 | # convert NHWC to NCWH 26 | ordered_result = [tf.transpose(v, [0, 3, 1, 2]) for v in ordered_result] 27 | # convert tf tensor to numpy 28 | results.append([item.numpy() for item in ordered_result]) 29 | 30 | return results 31 | 32 | def load_weights(self): 33 | self.model = tf.saved_model.load(osp.dirname(self.weights)) 34 | -------------------------------------------------------------------------------- /sscma/deploy/backend/tflite_infer.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | import torchvision.transforms as transforms 4 | 5 | import tensorflow as tf 6 | 7 | from .base_infer import BaseInfer 8 | class TFliteInfer(BaseInfer): 9 | 10 | def __init__(self, weights="sscma.tflite", device=torch.device("cpu")): 11 | super().__init__(weights=weights, device=device) 12 | self.interpreter = None 13 | self.input_details = None 14 | self.output_details = None 15 | 16 | def infer(self, input_data): 17 | results = [] 18 | for data in input_data.split(1, 0): 19 | # check if input_data is Tensor 20 | if isinstance(data, torch.Tensor): 21 | input = self.input_details[0] 22 | shape = input["shape"] 23 | if len(data.shape) == len(shape) and (data.shape[1] == 3 or data.shape[1] == 1): 24 | w = shape[2] 25 | h = shape[1] 26 | # resize to model input shape 27 | data = transforms.Resize((h, w))(data) 28 | # Torch Tensor convert NCWH to NHW 29 | data = data.permute(0, 2, 3, 1).numpy() 30 | 31 | int8 = ( 32 | input["dtype"] == np.uint8 or input["dtype"] == np.int8 33 | ) # is TFLite quantized uint8 model 34 | if int8: 35 | scale, zero_point = input["quantization"] 36 | data = (data / scale + zero_point).astype( 37 | input["dtype"] 38 | ) # de-scale 39 | self.interpreter.set_tensor(input["index"], data) 40 | self.interpreter.invoke() 41 | y = [] 42 | for output in self.output_details: 43 | x = self.interpreter.get_tensor(output["index"]) 44 | if int8: 45 | scale, zero_point = output["quantization"] 46 | x = (x.astype(np.float32) - zero_point) * scale # re-scale 47 | # numpy x convert NHWC to NCWH 48 | y.append(np.transpose(x, [0, 3, 1, 2]) if len(x.shape) == 4 else x) 49 | 50 | results.append(y) 51 | return results 52 | 53 | def load_weights(self): 54 | 55 | self.interpreter = tf.lite.Interpreter(model_path=self.weights) # load TFLite model 56 | 57 | self.interpreter.allocate_tensors() # allocate 58 | self.input_details = self.interpreter.get_input_details() # inputs 59 | self.output_details = self.interpreter.get_output_details() # outputs 60 | -------------------------------------------------------------------------------- /sscma/deploy/backend/torchscript_infer.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from .base_infer import BaseInfer 3 | 4 | 5 | class TorchScriptInfer(BaseInfer): 6 | def __init__(self, weights="sscma.torchscript", device=torch.device("cpu")): 7 | super().__init__(weights=weights, device=device) 8 | self.model = None 9 | 10 | def infer(self, input_data): 11 | results = [] 12 | for data in input_data.split(1, 0): 13 | results.append(self.model(data)) 14 | return results 15 | 16 | def load_weights(self): 17 | self.model = torch.jit.load(self.weights) 18 | -------------------------------------------------------------------------------- /sscma/deploy/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .rtmdet_infer import RTMDetInfer 2 | from .anomaly_infer import AnomalyInfer 3 | from .fomo_infer import FomoInfer 4 | from .pfld_infer import PFLDInfer 5 | 6 | __all__ = ["RTMDetInfer", "AnomalyInfer", "FomoInfer", "PFLDInfer"] 7 | -------------------------------------------------------------------------------- /sscma/deploy/models/anomaly_infer.py: -------------------------------------------------------------------------------- 1 | import copy 2 | 3 | import torch 4 | 5 | from mmengine import MODELS 6 | from mmengine.model import BaseModel 7 | from sscma.utils.typing_utils import OptConfigType, OptMultiConfig 8 | from ..backend import BaseInfer 9 | 10 | 11 | class AnomalyInfer(BaseModel): 12 | """AnomalyInfer class for Anomaly serial inference.""" 13 | 14 | def __init__( 15 | self, 16 | data_preprocessor: OptConfigType = None, 17 | init_cfg: OptMultiConfig = None, 18 | ): 19 | super().__init__(data_preprocessor=data_preprocessor, init_cfg=init_cfg) 20 | 21 | self.pred_head = None 22 | self.func = None 23 | self.config = None 24 | 25 | def forward( 26 | self, 27 | inputs: torch.Tensor, 28 | data_samples=None, 29 | mode: str = "predict", 30 | ): 31 | """The unified entry for a forward process in both training and test. 32 | The method should accept three modes: "tensor", "predict" and "loss": 33 | 34 | - "predict": Forward and return the predictions, which are fully 35 | processed to a list of :obj:`DetDataSample`. 36 | 37 | Note that this method doesn't handle either back propagation or 38 | parameter update, which are supposed to be done in :meth:`train_step`. 39 | 40 | Args: 41 | inputs (torch.Tensor): The input tensor with shape 42 | (N, C, ...) in general. 43 | data_samples (list[:obj:`DetDataSample`], optional): A batch of 44 | data samples that contain annotations and predictions. 45 | Defaults to None. 46 | mode (str): Return what kind of value. Defaults to 'tensor'. 47 | 48 | Returns: 49 | The return type depends on ``mode``. 50 | 51 | - ``mode="predict"``, return a list of :obj:`DetDataSample`. 52 | """ 53 | if mode == "predict": 54 | return self._predict(inputs, data_samples) 55 | else: 56 | raise RuntimeError( 57 | f'Invalid mode "{mode}". ' "AnomalyetInfer Only supports predict mode" 58 | ) 59 | 60 | def _predict(self, inputs: torch.Tensor, batch_data_samples=None): 61 | data = self.func.infer(inputs, split=False) 62 | res = [] 63 | for d in data: 64 | res.append(torch.from_numpy(d)) 65 | loss1, loss2, loss3 = self.vae_model.loss_function(*res) 66 | loss = loss1 + loss2 + loss3 67 | return [dict(loss=loss)] 68 | 69 | def set_infer(self, func: BaseInfer, Config: OptConfigType = None): 70 | self.func = func 71 | self.func.load_weights() 72 | if Config is not None: 73 | self.config = copy.deepcopy(Config) 74 | self.vae_model = MODELS.build(self.config.model) 75 | -------------------------------------------------------------------------------- /sscma/engine/__init__.py: -------------------------------------------------------------------------------- 1 | from .hooks import * # noqa: F401,F403 2 | -------------------------------------------------------------------------------- /sscma/engine/hooks/__init__.py: -------------------------------------------------------------------------------- 1 | from .memory_profiler_hook import MemoryProfilerHook 2 | from .pipeline_switch_hook import PipelineSwitchHook 3 | from .visualization_hook import DetVisualizationHook 4 | from .quantizer_switch_hook import QuantizerSwitchHook 5 | 6 | __all__ = ["MemoryProfilerHook", "PipelineSwitchHook", "DetVisualizationHook","QuantizerSwitchHook"] 7 | -------------------------------------------------------------------------------- /sscma/engine/hooks/pipeline_switch_hook.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from mmengine.dataset.base_dataset import Compose 3 | from mmengine.hooks import Hook 4 | 5 | 6 | class PipelineSwitchHook(Hook): 7 | """Switch data pipeline at switch_epoch. 8 | 9 | Args: 10 | switch_epoch (int): switch pipeline at this epoch. 11 | switch_pipeline (list[dict]): the pipeline to switch to. 12 | """ 13 | 14 | def __init__(self, switch_epoch, switch_pipeline): 15 | self.switch_epoch = switch_epoch 16 | self.switch_pipeline = switch_pipeline 17 | self._restart_dataloader = False 18 | self._has_switched = False 19 | 20 | def before_train_epoch(self, runner): 21 | """switch pipeline.""" 22 | epoch = runner.epoch 23 | train_loader = runner.train_dataloader 24 | if epoch >= self.switch_epoch and not self._has_switched: 25 | runner.logger.info("Switch pipeline now!") 26 | # The dataset pipeline cannot be updated when persistent_workers 27 | # is True, so we need to force the dataloader's multi-process 28 | # restart. This is a very hacky approach. 29 | train_loader.dataset.pipeline = Compose(self.switch_pipeline) 30 | if ( 31 | hasattr(train_loader, "persistent_workers") 32 | and train_loader.persistent_workers is True 33 | ): 34 | train_loader._DataLoader__initialized = False 35 | train_loader._iterator = None 36 | self._restart_dataloader = True 37 | self._has_switched = True 38 | else: 39 | # Once the restart is complete, we need to restore 40 | # the initialization flag. 41 | if self._restart_dataloader: 42 | train_loader._DataLoader__initialized = True 43 | -------------------------------------------------------------------------------- /sscma/engine/hooks/quantizer_switch_hook.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import torch 3 | from mmengine.hooks import Hook 4 | from mmengine import HOOKS 5 | 6 | @HOOKS.register_module() 7 | class QuantizerSwitchHook(Hook): 8 | """Switch data pipeline at switch_epoch. 9 | 10 | Args: 11 | freeze_quantizer_epoch (int): the epoch to freeze the quantizer. 12 | freeze_bn_epoch (int): the epoch to freeze the batch normalization 13 | statistics. 14 | """ 15 | 16 | def __init__(self,freeze_quantizer_epoch, freeze_bn_epoch): 17 | self.freeze_quantizer_epoch = freeze_quantizer_epoch 18 | self.freeze_bn_epoch = freeze_bn_epoch 19 | 20 | 21 | def before_train_epoch(self, runner): 22 | """switch pipeline.""" 23 | epoch = runner.epoch 24 | #if epoch == runner.max_epoch // 3: 25 | if epoch == self.freeze_quantizer_epoch: 26 | runner.logger.info("freeze quantizer parameters") 27 | runner.model.apply(torch.quantization.disable_observer) 28 | #elif epoch == runner.max_epoch // 3 * 2: 29 | elif epoch == self.freeze_bn_epoch: 30 | runner.logger.info("freeze batch norm mean and variance estimates") 31 | runner.model.apply(torch.nn.intrinsic.qat.freeze_bn_stats) -------------------------------------------------------------------------------- /sscma/engine/schedulers/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .quadratic_warmup import (QuadraticWarmupLR, QuadraticWarmupMomentum, 3 | QuadraticWarmupParamScheduler) 4 | 5 | __all__ = [ 6 | 'QuadraticWarmupParamScheduler', 'QuadraticWarmupMomentum', 7 | 'QuadraticWarmupLR' 8 | ] 9 | -------------------------------------------------------------------------------- /sscma/evaluation/__init__.py: -------------------------------------------------------------------------------- 1 | from .base_metric import BaseMetric 2 | from .bbox_overlaps import bbox_overlaps 3 | from .coco_metric import CocoMetric 4 | from .dist_backends import ( 5 | BaseDistBackend, 6 | NonDist, 7 | TensorBaseDistBackend, 8 | TorchCPUDist, 9 | TorchCUDADist, 10 | ) 11 | from .dist import list_all_backends, set_default_dist_backend, get_dist_backend 12 | from .evaluator import Evaluator 13 | from .metrics import Accuracy 14 | from .panoptic_utils import pq_compute_single_core, pq_compute_multi_core 15 | from .recall import ( 16 | _recalls, 17 | set_recall_param, 18 | eval_recalls, 19 | print_recall_summary, 20 | plot_num_recall, 21 | plot_iou_recall, 22 | ) 23 | from .point_metric import PointMetric 24 | from .fomo_metric import FomoMetric 25 | from .mse_metric import MseMetric 26 | 27 | __all__ = [ 28 | "BaseMetric", 29 | "bbox_overlaps", 30 | "CocoMetric", 31 | "BaseDistBackend", 32 | "NonDist", 33 | "TensorBaseDistBackend", 34 | "TorchCPUDist", 35 | "TorchCUDADist", 36 | "list_all_backends", 37 | "set_default_dist_backend", 38 | "get_dist_backend", 39 | "Evaluator", 40 | "Accuracy", 41 | "pq_compute_single_core", 42 | "pq_compute_multi_core", 43 | "_recalls", 44 | "set_recall_param", 45 | "eval_recalls", 46 | "print_recall_summary", 47 | "plot_num_recall", 48 | "plot_iou_recall", 49 | "PointMetric", 50 | "FomoMetric", 51 | "MseMetric", 52 | ] 53 | -------------------------------------------------------------------------------- /sscma/evaluation/bbox_overlaps.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import numpy as np 3 | 4 | 5 | def bbox_overlaps(bboxes1, bboxes2, mode="iou", eps=1e-6, use_legacy_coordinate=False): 6 | """Calculate the ious between each bbox of bboxes1 and bboxes2. 7 | 8 | Args: 9 | bboxes1 (ndarray): Shape (n, 4) 10 | bboxes2 (ndarray): Shape (k, 4) 11 | mode (str): IOU (intersection over union) or IOF (intersection 12 | over foreground) 13 | use_legacy_coordinate (bool): Whether to use coordinate system in 14 | mmdet v1.x. which means width, height should be 15 | calculated as 'x2 - x1 + 1` and 'y2 - y1 + 1' respectively. 16 | Note when function is used in `VOCDataset`, it should be 17 | True to align with the official implementation 18 | `http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCdevkit_18-May-2011.tar` 19 | Default: False. 20 | 21 | Returns: 22 | ious (ndarray): Shape (n, k) 23 | """ 24 | 25 | assert mode in ["iou", "iof"] 26 | if not use_legacy_coordinate: 27 | extra_length = 0.0 28 | else: 29 | extra_length = 1.0 30 | bboxes1 = bboxes1.astype(np.float32) 31 | bboxes2 = bboxes2.astype(np.float32) 32 | rows = bboxes1.shape[0] 33 | cols = bboxes2.shape[0] 34 | ious = np.zeros((rows, cols), dtype=np.float32) 35 | if rows * cols == 0: 36 | return ious 37 | exchange = False 38 | if bboxes1.shape[0] > bboxes2.shape[0]: 39 | bboxes1, bboxes2 = bboxes2, bboxes1 40 | ious = np.zeros((cols, rows), dtype=np.float32) 41 | exchange = True 42 | area1 = (bboxes1[:, 2] - bboxes1[:, 0] + extra_length) * ( 43 | bboxes1[:, 3] - bboxes1[:, 1] + extra_length 44 | ) 45 | area2 = (bboxes2[:, 2] - bboxes2[:, 0] + extra_length) * ( 46 | bboxes2[:, 3] - bboxes2[:, 1] + extra_length 47 | ) 48 | for i in range(bboxes1.shape[0]): 49 | x_start = np.maximum(bboxes1[i, 0], bboxes2[:, 0]) 50 | y_start = np.maximum(bboxes1[i, 1], bboxes2[:, 1]) 51 | x_end = np.minimum(bboxes1[i, 2], bboxes2[:, 2]) 52 | y_end = np.minimum(bboxes1[i, 3], bboxes2[:, 3]) 53 | overlap = np.maximum(x_end - x_start + extra_length, 0) * np.maximum( 54 | y_end - y_start + extra_length, 0 55 | ) 56 | if mode == "iou": 57 | union = area1[i] + area2 - overlap 58 | else: 59 | union = area1[i] if not exchange else area2 60 | union = np.maximum(union, eps) 61 | ious[i, :] = overlap / union 62 | if exchange: 63 | ious = ious.T 64 | return ious 65 | -------------------------------------------------------------------------------- /sscma/evaluation/dist.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from typing import List, Optional, no_type_check 3 | 4 | from .dist_backends import BaseDistBackend, NonDist, TorchCPUDist, TorchCUDADist 5 | 6 | _DIST_BACKENDS = { 7 | "non_dist": NonDist, 8 | "torch_cpu": TorchCPUDist, 9 | "torch_cuda": TorchCUDADist, 10 | } 11 | 12 | _DEFAULT_BACKEND = "non_dist" 13 | 14 | # Caching created dist backend instances 15 | _DIST_BACKEND_INSTANCES: dict = {} 16 | 17 | 18 | def list_all_backends() -> List[str]: 19 | """Returns a list of all distributed backend names. 20 | 21 | Returns: 22 | List[str]: A list of all distributed backend names. 23 | """ 24 | return list(_DIST_BACKENDS.keys()) 25 | 26 | 27 | def set_default_dist_backend(dist_backend: str) -> None: 28 | """Set the given distributed backend as the default distributed backend. 29 | 30 | Args: 31 | dist_backend (str): The distribute backend name to set. 32 | """ 33 | assert dist_backend in _DIST_BACKENDS 34 | global _DEFAULT_BACKEND 35 | _DEFAULT_BACKEND = dist_backend 36 | 37 | 38 | @no_type_check 39 | def get_dist_backend(dist_backend: Optional[str] = None) -> BaseDistBackend: 40 | """Returns distributed backend by the given distributed backend name. 41 | 42 | Args: 43 | dist_backend (str, optional): The distributed backend name want to get. 44 | if None, return the default distributed backend. 45 | 46 | Returns: 47 | :obj:`BaseDistBackend`: The distributed backend instance. 48 | """ 49 | if dist_backend is None: 50 | dist_backend = _DEFAULT_BACKEND 51 | assert dist_backend in _DIST_BACKENDS 52 | if dist_backend not in _DIST_BACKEND_INSTANCES: 53 | dist_backend_cls = _DIST_BACKENDS[dist_backend] 54 | _DIST_BACKEND_INSTANCES[dist_backend] = dist_backend_cls() 55 | return _DIST_BACKEND_INSTANCES[dist_backend] 56 | -------------------------------------------------------------------------------- /sscma/evaluation/evaluator.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | """This module is used to patch the default Evaluator in MMEngine. 3 | 4 | For the convenient of customizing the Metric, ``sscma`` patch 5 | the ``mmengine.evaluator.Evaluator`` with the local ``Evaluator`` Thanks to 6 | this, the Metric in sscma only needs to implement the ``add`` and 7 | ``compute_metric``. 8 | 9 | Warning: 10 | If there is a need to customize the Evaluator for more complicated evaluate 11 | process. The methods defined in ``CustomEvaluator`` must call 12 | ``metric.compute`` and ``metric.add`` rather than ``metric.process`` and 13 | ``metric.evaluate`` 14 | """ 15 | 16 | from mmengine.evaluator import Evaluator as MMEngineEvaluator 17 | from mmengine.structures import BaseDataElement 18 | 19 | 20 | class Evaluator(MMEngineEvaluator): 21 | def process(self, data_samples, data_batch=None): 22 | _data_samples = [] 23 | for data_sample in data_samples: 24 | if isinstance(data_sample, BaseDataElement): 25 | _data_samples.append(data_sample.to_dict()) 26 | else: 27 | _data_samples.append(data_sample) 28 | 29 | for metric in self.metrics: 30 | metric.add(data_batch, _data_samples) 31 | 32 | def evaluate(self, size): 33 | metrics = {} 34 | for metric in self.metrics: 35 | _results = metric.compute(size) 36 | 37 | # Check metric name conflicts 38 | for name in _results.keys(): 39 | if name in metrics: 40 | raise ValueError( 41 | "There are multiple evaluation results with the same " 42 | f"metric name {name}. Please make sure all metrics " 43 | "have different prefixes." 44 | ) 45 | 46 | metrics.update(_results) 47 | return metrics 48 | -------------------------------------------------------------------------------- /sscma/evaluation/mse_metric.py: -------------------------------------------------------------------------------- 1 | from typing import Any, Sequence 2 | 3 | import numpy as np 4 | from mmengine.evaluator import BaseMetric 5 | 6 | 7 | class MseMetric(BaseMetric): 8 | """The MSE metric for evaluating the quality of the predictions. 9 | 10 | Args: 11 | dist_sync_on_step (bool): Whether to synchronize the metric states 12 | across processes at each ``forward()`` call. Defaults to False. 13 | """ 14 | 15 | def __init__(self): 16 | super().__init__() 17 | 18 | def add(self, pred: float, target: float) -> None: 19 | """Add the metric result. 20 | 21 | Args: 22 | pred (float): The predicted value. 23 | target (float): The ground truth value. 24 | """ 25 | self._results.append((pred - target) ** 2) 26 | 27 | def process(self, data_batch: Any, data_samples: Sequence[dict]) -> None: 28 | self.results.append(data_samples[0]["loss"].cpu().numpy()) 29 | 30 | def compute(self) -> float: 31 | """Compute the metric. 32 | 33 | Returns: 34 | float: The computed metric. 35 | """ 36 | return dict(mse=np.sum(self.results) / len(self.results)) 37 | 38 | def compute_metrics(self, results: list) -> dict: 39 | """Compute the metrics from processed results. 40 | 41 | Args: 42 | results (list): The processed results of each batch. 43 | 44 | Returns: 45 | dict: The computed metrics. 46 | """ 47 | return dict(mse=np.sum(self.results) / len(self.results)) 48 | -------------------------------------------------------------------------------- /sscma/evaluation/point_metric.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Seeed Technology Co.,Ltd. All rights reserved. 2 | from typing import Any, Optional, Sequence 3 | 4 | import numpy as np 5 | from mmengine.evaluator import BaseMetric 6 | 7 | 8 | def pose_acc(pred, target, hw, th=10): 9 | h = hw[0] if isinstance(hw[0], int) else int(hw[0][0]) 10 | w = hw[1] if isinstance(hw[1], int) else int(hw[1][0]) 11 | pred[:, 0::2] = ( 12 | pred[:, 0::2] * w 13 | ) # TypeError: unsupported operand type(s) for *: 'generator' and 'int' 14 | pred[:, 1::2] = pred[:, 1::2] * h 15 | pred[pred < 0] = 0 16 | 17 | target[:, 0::2] = target[:, 0::2] * w 18 | target[:, 1::2] = target[:, 1::2] * h 19 | 20 | th = th 21 | acc = [] 22 | for p, t in zip(pred, target): 23 | distans = ((t[0] - p[0]) ** 2 + (t[1] - p[1]) ** 2) ** 0.5 24 | if distans > th: 25 | acc.append(0) 26 | elif distans > 1: 27 | acc.append((th - distans) / (th - 1)) 28 | else: 29 | acc.append(1) 30 | return sum(acc) / len(acc) 31 | 32 | 33 | class PointMetric(BaseMetric): 34 | def __init__( 35 | self, collect_device: str = "cpu", prefix: Optional[str] = "keypoint" 36 | ) -> None: 37 | super().__init__(collect_device, prefix) 38 | 39 | def process(self, data_batch: Any, data_samples: Sequence[dict]) -> None: 40 | target = data_batch["data_samples"]["keypoints"] 41 | size = data_batch["data_samples"]["hw"] # .cpu().numpy() 42 | result = np.array( 43 | [ 44 | i if isinstance(i, np.ndarray) else i.cpu().numpy() 45 | for i in data_samples[0]["results"] 46 | ] 47 | ) 48 | 49 | result = ( 50 | result if len(result.shape) == 2 else result[None, :] 51 | ) # onnx shape(2,), tflite shape(1,2) 52 | acc = pose_acc(result.copy(), target, size) 53 | self.results.append( 54 | { 55 | "Acc": acc, 56 | "pred": result, 57 | "image_file": data_batch["data_samples"]["image_file"], 58 | } 59 | ) 60 | 61 | def compute_metrics(self, results: list) -> dict: 62 | return {"Acc": sum([i["Acc"] for i in results]) / len(results)} 63 | -------------------------------------------------------------------------------- /sscma/infer/__init__.py: -------------------------------------------------------------------------------- 1 | from .inference import CustomInferencer 2 | 3 | __all__ = ["CustomInferencer"] 4 | -------------------------------------------------------------------------------- /sscma/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .backbones import * # noqa: F401,F403 2 | from .classifiers import * # noqa: F401,F403 3 | from .cnn import * # noqa: F401,F403 4 | from .detectors import * # noqa: F401,F403 5 | from .heads import * # noqa: F401,F403 6 | from .layers import * # noqa: F401,F403 7 | from .losses import * # noqa: F401,F403 8 | from .necks import * # noqa: F401,F403 9 | from .task_modules import * # noqa: F401,F403 10 | from .test_time_augs import * # noqa: F401,F403 11 | from .utils import * # noqa: F401,F403 12 | -------------------------------------------------------------------------------- /sscma/models/backbones/__init__.py: -------------------------------------------------------------------------------- 1 | from .csp_darknet import CSPDarknet, Focus, YOLOv5CSPDarknet 2 | from .cspnext import CSPNeXt 3 | from .timm import TimmBackbone 4 | from .mobilenetv2 import PfldMobileNetV2, MobileNetv2 5 | from .base_backbone import YOLOBaseBackbone 6 | from .mobilenetv3 import MobileNetV3 7 | 8 | 9 | __all__ = [ 10 | "CSPDarknet", 11 | "Focus", 12 | "CSPNeXt", 13 | "TimmBackbone", 14 | "PfldMobileNetV2", 15 | "MobileNetv2", 16 | "YOLOBaseBackbone", 17 | "YOLOv5CSPDarknet", 18 | "MobileNetV3", 19 | ] 20 | -------------------------------------------------------------------------------- /sscma/models/base/__init__.py: -------------------------------------------------------------------------------- 1 | from .general import ( 2 | CBR, 3 | ConvNormActivation, 4 | get_act, 5 | get_conv, 6 | get_norm, 7 | InvertedResidual, 8 | ) 9 | 10 | __all__ = [ 11 | "CBR", 12 | "ConvNormActivation", 13 | "get_act", 14 | "get_conv", 15 | "get_norm", 16 | "InvertedResidual", 17 | ] 18 | -------------------------------------------------------------------------------- /sscma/models/classifiers/__init__.py: -------------------------------------------------------------------------------- 1 | from .image import ImageClassifier 2 | 3 | 4 | __all__ = ["ImageClassifier"] 5 | -------------------------------------------------------------------------------- /sscma/models/cnn/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | # from .activation import build_activation_layer 3 | from .activation import build_activation_layer, Clamp, GELU 4 | from .conv import build_conv_layer 5 | from .conv_module import ConvModule 6 | from .depthwise_separable_conv_module import DepthwiseSeparableConvModule 7 | from .drop import Dropout, DropPath 8 | from .norm import build_norm_layer, is_norm 9 | from .padding import build_padding_layer 10 | from .scale import Scale 11 | 12 | from .conv_block import Conv_block1D, Conv_block2D 13 | 14 | __all__ = [ 15 | "build_activation_layer", 16 | "Clamp", 17 | "GELU", 18 | "ConvModule", 19 | "Scale", 20 | "build_conv_layer", 21 | "build_padding_layer", 22 | "DepthwiseSeparableConvModule", 23 | "build_norm_layer", 24 | "is_norm", 25 | "Dropout", 26 | "DropPath", 27 | "Conv_block1D", 28 | "Conv_block2D", 29 | ] 30 | -------------------------------------------------------------------------------- /sscma/models/cnn/activation.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from typing import Dict 3 | 4 | import torch 5 | import torch.nn as nn 6 | import torch.nn.functional as F 7 | 8 | from mmengine.registry import MODELS 9 | 10 | 11 | class Clamp(nn.Module): 12 | """Clamp activation layer. 13 | 14 | This activation function is to clamp the feature map value within 15 | :math:`[min, max]`. More details can be found in ``torch.clamp()``. 16 | 17 | Args: 18 | min (Number | optional): Lower-bound of the range to be clamped to. 19 | Default to -1. 20 | max (Number | optional): Upper-bound of the range to be clamped to. 21 | Default to 1. 22 | """ 23 | 24 | def __init__(self, min: float = -1.0, max: float = 1.0): 25 | super().__init__() 26 | self.min = min 27 | self.max = max 28 | 29 | def forward(self, x) -> torch.Tensor: 30 | """Forward function. 31 | 32 | Args: 33 | x (torch.Tensor): The input tensor. 34 | 35 | Returns: 36 | torch.Tensor: Clamped tensor. 37 | """ 38 | return torch.clamp(x, min=self.min, max=self.max) 39 | 40 | 41 | class GELU(nn.Module): 42 | r"""Applies the Gaussian Error Linear Units function: 43 | 44 | .. math:: 45 | \text{GELU}(x) = x * \Phi(x) 46 | where :math:`\Phi(x)` is the Cumulative Distribution Function for 47 | Gaussian Distribution. 48 | 49 | Shape: 50 | - Input: :math:`(N, *)` where `*` means, any number of additional 51 | dimensions 52 | - Output: :math:`(N, *)`, same shape as the input 53 | 54 | .. image:: scripts/activation_images/GELU.png 55 | 56 | Examples:: 57 | 58 | >>> m = nn.GELU() 59 | >>> input = torch.randn(2) 60 | >>> output = m(input) 61 | """ 62 | 63 | def forward(self, input: torch.Tensor) -> torch.Tensor: 64 | return F.gelu(input) 65 | 66 | 67 | def build_activation_layer(cfg: Dict) -> nn.Module: 68 | """Build activation layer. 69 | 70 | Args: 71 | cfg (dict): The activation layer config, which should contain: 72 | 73 | - type (str): Layer type. 74 | - layer args: Args needed to instantiate an activation layer. 75 | 76 | Returns: 77 | nn.Module: Created activation layer. 78 | """ 79 | return MODELS.build(cfg) 80 | -------------------------------------------------------------------------------- /sscma/models/cnn/conv.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import inspect 3 | from typing import Dict, Optional 4 | from torch import nn 5 | 6 | 7 | from mmengine.registry import MODELS 8 | 9 | 10 | def build_conv_layer(cfg: Optional[Dict], *args, **kwargs) -> nn.Module: 11 | """Build convolution layer. 12 | 13 | Args: 14 | cfg (None or dict): The conv layer config, which should contain: 15 | - type (str): Layer type. 16 | - layer args: Args needed to instantiate an conv layer. 17 | args (argument list): Arguments passed to the `__init__` 18 | method of the corresponding conv layer. 19 | kwargs (keyword arguments): Keyword arguments passed to the `__init__` 20 | method of the corresponding conv layer. 21 | 22 | Returns: 23 | nn.Module: Created conv layer. 24 | """ 25 | if cfg is None: 26 | cfg_ = dict(type=nn.Conv2d) 27 | else: 28 | if not isinstance(cfg, dict): 29 | raise TypeError("cfg must be a dict") 30 | if "type" not in cfg: 31 | raise KeyError('the cfg dict must contain the key "type"') 32 | cfg_ = cfg.copy() 33 | 34 | layer_type = cfg_.pop("type") 35 | if inspect.isclass(layer_type): 36 | return layer_type(*args, **kwargs, **cfg_) # type: ignore 37 | # Switch registry to the target scope. If `conv_layer` cannot be found 38 | # in the registry, fallback to search `conv_layer` in the 39 | # mmengine.MODELS. 40 | with MODELS.switch_scope_and_registry(None) as registry: 41 | conv_layer = registry.get(layer_type) 42 | if conv_layer is None: 43 | raise KeyError( 44 | f"Cannot find {conv_layer} in registry under scope " 45 | f"name {registry.scope}" 46 | ) 47 | layer = conv_layer(*args, **kwargs, **cfg_) 48 | 49 | return layer 50 | -------------------------------------------------------------------------------- /sscma/models/cnn/drop.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from typing import Any, Dict, Optional 3 | 4 | import torch 5 | import torch.nn as nn 6 | 7 | from mmengine.registry import MODELS 8 | 9 | 10 | def drop_path( 11 | x: torch.Tensor, drop_prob: float = 0.0, training: bool = False 12 | ) -> torch.Tensor: 13 | """Drop paths (Stochastic Depth) per sample (when applied in main path of 14 | residual blocks). 15 | 16 | We follow the implementation 17 | https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 18 | """ 19 | if drop_prob == 0.0 or not training: 20 | return x 21 | keep_prob = 1 - drop_prob 22 | # handle tensors with different dimensions, not just 4D tensors. 23 | shape = (x.shape[0],) + (1,) * (x.ndim - 1) 24 | random_tensor = keep_prob + torch.rand(shape, dtype=x.dtype, device=x.device) 25 | output = x.div(keep_prob) * random_tensor.floor() 26 | return output 27 | 28 | 29 | class DropPath(nn.Module): 30 | """Drop paths (Stochastic Depth) per sample (when applied in main path of 31 | residual blocks). 32 | 33 | We follow the implementation 34 | https://github.com/rwightman/pytorch-image-models/blob/a2727c1bf78ba0d7b5727f5f95e37fb7f8866b1f/timm/models/layers/drop.py # noqa: E501 35 | 36 | Args: 37 | drop_prob (float): Probability of the path to be zeroed. Default: 0.1 38 | """ 39 | 40 | def __init__(self, drop_prob: float = 0.1): 41 | super().__init__() 42 | self.drop_prob = drop_prob 43 | 44 | def forward(self, x: torch.Tensor) -> torch.Tensor: 45 | return drop_path(x, self.drop_prob, self.training) 46 | 47 | 48 | class Dropout(nn.Dropout): 49 | """A wrapper for ``torch.nn.Dropout``, We rename the ``p`` of 50 | ``torch.nn.Dropout`` to ``drop_prob`` so as to be consistent with 51 | ``DropPath`` 52 | 53 | Args: 54 | drop_prob (float): Probability of the elements to be 55 | zeroed. Default: 0.5. 56 | inplace (bool): Do the operation inplace or not. Default: False. 57 | """ 58 | 59 | def __init__(self, drop_prob: float = 0.5, inplace: bool = False): 60 | super().__init__(p=drop_prob, inplace=inplace) 61 | 62 | 63 | def build_dropout(cfg: Dict, default_args: Optional[Dict] = None) -> Any: 64 | """Builder for drop out layers.""" 65 | return MODELS.build(cfg, default_args=default_args) 66 | -------------------------------------------------------------------------------- /sscma/models/cnn/padding.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import inspect 3 | from typing import Dict 4 | 5 | import torch.nn as nn 6 | from mmengine.registry import MODELS 7 | 8 | 9 | def build_padding_layer(cfg: Dict, *args, **kwargs) -> nn.Module: 10 | """Build padding layer. 11 | 12 | Args: 13 | cfg (dict): The padding layer config, which should contain: 14 | - type (str): Layer type. 15 | - layer args: Args needed to instantiate a padding layer. 16 | 17 | Returns: 18 | nn.Module: Created padding layer. 19 | """ 20 | if not isinstance(cfg, dict): 21 | raise TypeError("cfg must be a dict") 22 | if "type" not in cfg: 23 | raise KeyError('the cfg dict must contain the key "type"') 24 | 25 | cfg_ = cfg.copy() 26 | padding_type = cfg_.pop("type") 27 | if inspect.isclass(padding_type): 28 | return padding_type(*args, **kwargs, **cfg_) 29 | # Switch registry to the target scope. If `padding_layer` cannot be found 30 | # in the registry, fallback to search `padding_layer` in the 31 | # mmengine.MODELS. 32 | with MODELS.switch_scope_and_registry(None) as registry: 33 | padding_layer = registry.get(padding_type) 34 | if padding_layer is None: 35 | raise KeyError( 36 | f"Cannot find {padding_layer} in registry under scope " 37 | f"name {registry.scope}" 38 | ) 39 | layer = padding_layer(*args, **kwargs, **cfg_) 40 | 41 | return layer 42 | -------------------------------------------------------------------------------- /sscma/models/cnn/scale.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import torch 3 | import torch.nn as nn 4 | 5 | 6 | class Scale(nn.Module): 7 | """A learnable scale parameter. 8 | 9 | This layer scales the input by a learnable factor. It multiplies a 10 | learnable scale parameter of shape (1,) with input of any shape. 11 | 12 | Args: 13 | scale (float): Initial value of scale factor. Default: 1.0 14 | """ 15 | 16 | def __init__(self, scale: float = 1.0): 17 | super().__init__() 18 | self.scale = nn.Parameter(torch.tensor(scale, dtype=torch.float)) 19 | 20 | def forward(self, x: torch.Tensor) -> torch.Tensor: 21 | return x * self.scale 22 | 23 | 24 | class LayerScale(nn.Module): 25 | """LayerScale layer. 26 | 27 | Args: 28 | dim (int): Dimension of input features. 29 | inplace (bool): Whether performs operation in-place. 30 | Default: `False`. 31 | data_format (str): The input data format, could be 'channels_last' 32 | or 'channels_first', representing (B, C, H, W) and 33 | (B, N, C) format data respectively. Default: 'channels_last'. 34 | scale (float): Initial value of scale factor. Default: 1.0 35 | """ 36 | 37 | def __init__( 38 | self, 39 | dim: int, 40 | inplace: bool = False, 41 | data_format: str = "channels_last", 42 | scale: float = 1e-5, 43 | ): 44 | super().__init__() 45 | assert data_format in ( 46 | "channels_last", 47 | "channels_first", 48 | ), "'data_format' could only be channels_last or channels_first." 49 | self.inplace = inplace 50 | self.data_format = data_format 51 | self.weight = nn.Parameter(torch.ones(dim) * scale) 52 | 53 | def forward(self, x) -> torch.Tensor: 54 | if self.data_format == "channels_first": 55 | shape = tuple((1, -1, *(1 for _ in range(x.dim() - 2)))) 56 | else: 57 | shape = tuple((*(1 for _ in range(x.dim() - 1)), -1)) 58 | if self.inplace: 59 | return x.mul_(self.weight.view(*shape)) 60 | else: 61 | return x * self.weight.view(*shape) 62 | -------------------------------------------------------------------------------- /sscma/models/detectors/__init__.py: -------------------------------------------------------------------------------- 1 | from .base import BaseDetector 2 | from .rtmdet import RTMDet 3 | from .single_stage import SingleStageDetector 4 | from .pfld import PFLD 5 | from .fomo import Fomo 6 | from .anomaly import Vae_Model 7 | 8 | __all__ = ["BaseDetector", "RTMDet", "SingleStageDetector", "PFLD", "Fomo", "Vae_Model"] 9 | -------------------------------------------------------------------------------- /sscma/models/detectors/pfld.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Seeed Technology Co.,Ltd. All rights reserved. 2 | from typing import Optional, Tuple 3 | 4 | import numpy as np 5 | from torch import Tensor 6 | 7 | from mmengine import MODELS 8 | from mmengine.structures.instance_data import InstanceData 9 | from mmengine.model import BaseModel 10 | from sscma.structures import PoseDataSample 11 | 12 | 13 | class PFLD(BaseModel): 14 | """ 15 | PFLD: A Practical Facial Landmark Detector: https://arxiv.org/abs/1902.10859 16 | Args: 17 | backbone(dict): Configuration of pfld model backbone 18 | head(dict): Configuration of pfld model head 19 | pretrained: Model pre-training weight path 20 | """ 21 | 22 | def __init__(self, backbone: dict, head: dict, pretrained: Optional[str] = None): 23 | super(PFLD, self).__init__() 24 | self.backbone = MODELS.build(backbone) 25 | self.head = MODELS.build(head) 26 | self.pretrained = pretrained 27 | 28 | def forward(self, inputs, data_samples=None, mode="tensor"): 29 | if mode == "loss": 30 | return self.loss(inputs, data_samples) 31 | elif mode == "predict": 32 | return self.predict(inputs, data_samples) 33 | elif mode == "tensor": 34 | return self.forward_(inputs, data_samples) 35 | else: 36 | raise ValueError(f"params mode receive a not exception params:{mode}") 37 | 38 | def loss(self, inputs, data_samples): 39 | x = self.extract_feat(inputs) 40 | results = dict() 41 | results.update(self.head.loss(x, data_samples)) 42 | return results 43 | 44 | def predict(self, inputs, data_samples): 45 | feat = self.extract_feat(inputs) 46 | x = self.head.predict(feat) 47 | res = PoseDataSample(**data_samples) 48 | res.results = x 49 | res.pred_instances = InstanceData( 50 | keypoints=np.array([x.reshape(-1, 2).cpu().numpy()]) 51 | * data_samples["init_size"][1].reshape(-1, 1).cpu().numpy() 52 | ) 53 | 54 | return [res] 55 | 56 | def forward_(self, inputs, data_samples): 57 | x = self.extract_feat(inputs) 58 | return self.head(x) 59 | 60 | def extract_feat(self, inputs: Tensor) -> Tuple[Tensor]: 61 | x = self.backbone(inputs) 62 | if self.with_neck: 63 | x = self.neck(x) 64 | 65 | return x 66 | 67 | @property 68 | def with_neck(self) -> bool: 69 | return hasattr(self, "neck") and self.neck is not None 70 | -------------------------------------------------------------------------------- /sscma/models/detectors/rtmdet.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from typing import List, Tuple, Union 3 | import torch 4 | from torch import Tensor 5 | 6 | from mmengine.dist import get_world_size 7 | from mmengine.logging import print_log 8 | 9 | from sscma.structures import SampleList 10 | from sscma.utils.typing_utils import ConfigType, OptConfigType, OptMultiConfig 11 | from .single_stage import SingleStageDetector 12 | 13 | 14 | class RTMDet(SingleStageDetector): 15 | """Implementation of RTMDet. 16 | 17 | Args: 18 | backbone (:obj:`ConfigDict` or dict): The backbone module. 19 | neck (:obj:`ConfigDict` or dict): The neck module. 20 | bbox_head (:obj:`ConfigDict` or dict): The bbox head module. 21 | train_cfg (:obj:`ConfigDict` or dict, optional): The training config 22 | of ATSS. Defaults to None. 23 | test_cfg (:obj:`ConfigDict` or dict, optional): The testing config 24 | of ATSS. Defaults to None. 25 | data_preprocessor (:obj:`ConfigDict` or dict, optional): Config of 26 | :class:`DetDataPreprocessor` to process the input data. 27 | Defaults to None. 28 | init_cfg (:obj:`ConfigDict` or dict, optional): the config to control 29 | the initialization. Defaults to None. 30 | use_syncbn (bool): Whether to use SyncBatchNorm. Defaults to True. 31 | """ 32 | 33 | def __init__( 34 | self, 35 | backbone: ConfigType, 36 | neck: ConfigType, 37 | bbox_head: ConfigType, 38 | train_cfg: OptConfigType = None, 39 | test_cfg: OptConfigType = None, 40 | data_preprocessor: OptConfigType = None, 41 | init_cfg: OptMultiConfig = None, 42 | use_syncbn: bool = True, 43 | ) -> None: 44 | super().__init__( 45 | backbone=backbone, 46 | neck=neck, 47 | bbox_head=bbox_head, 48 | train_cfg=train_cfg, 49 | test_cfg=test_cfg, 50 | data_preprocessor=data_preprocessor, 51 | init_cfg=init_cfg, 52 | ) 53 | 54 | # TODO: Waiting for mmengine support 55 | if use_syncbn and get_world_size() > 1: 56 | torch.nn.SyncBatchNorm.convert_sync_batchnorm(self) 57 | print_log("Using SyncBatchNorm()", "current") 58 | -------------------------------------------------------------------------------- /sscma/models/heads/__init__.py: -------------------------------------------------------------------------------- 1 | from .anchor_head import AnchorHead, AnchorGenerator 2 | from .atss_head import ATSSHead, AnchorHead 3 | from .base_dense_head import BaseDenseHead 4 | from .cls_head import LinearClsHead 5 | from .rtmdet_head import RTMDetHead, RTMDetSepBNHeadModule 6 | from .pfld_head import PFLDhead 7 | from .fomo_head import FomoHead 8 | 9 | __all__ = [ 10 | "AnchorHead", 11 | "AnchorGenerator", 12 | "ATSSHead", 13 | "BaseDenseHead", 14 | "LinearClsHead", 15 | "RTMDetHead", 16 | "RTMDetSepBNHeadModule", 17 | "PFLDhead", 18 | "FomoHead", 19 | ] 20 | -------------------------------------------------------------------------------- /sscma/models/layers/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .csp_layer import CSPLayer 3 | from .ema import ExpMomentumEMA, ExponentialMovingAverage 4 | from .se_layer import ChannelAttention, DyReLU, SELayer 5 | from .utils import ( 6 | nlc_to_nchw, 7 | nchw_to_nlc, 8 | coordinate_to_encoding, 9 | inverse_sigmoid, 10 | get_text_sine_pos_embed, 11 | AdaptivePadding, 12 | PatchEmbed, 13 | PatchMerging, 14 | ConditionalAttention, 15 | MLP, 16 | DynamicConv, 17 | ) 18 | from .rep import RepBlock, RepConv1x1, RepLine, VanillaBlock, Activation 19 | from .encode import Vae_Encode 20 | from .decode import Vae_Decode 21 | 22 | __all__ = [ 23 | "CSPLayer", 24 | "ExpMomentumEMA", 25 | "ExponentialMovingAverage", 26 | "ChannelAttention", 27 | "DyReLU", 28 | "SELayer", 29 | "nlc_to_nchw", 30 | "nchw_to_nlc", 31 | "coordinate_to_encoding", 32 | "inverse_sigmoid", 33 | "get_text_sine_pos_embed", 34 | "AdaptivePadding", 35 | "PatchEmbed", 36 | "PatchMerging", 37 | "ConditionalAttention", 38 | "MLP", 39 | "DynamicConv", 40 | "RepBlock", 41 | "RepConv1x1", 42 | "RepLine", 43 | "VanillaBlock", 44 | "Activation", 45 | "Vae_Encode", 46 | "Vae_Decode", 47 | ] 48 | -------------------------------------------------------------------------------- /sscma/models/layers/ema.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import math 3 | from typing import Optional 4 | 5 | import torch 6 | import torch.nn as nn 7 | from torch import Tensor 8 | 9 | from mmengine.model import ExponentialMovingAverage 10 | 11 | 12 | class ExpMomentumEMA(ExponentialMovingAverage): 13 | """Exponential moving average (EMA) with exponential momentum strategy, 14 | which is used in YOLOX. 15 | 16 | Args: 17 | model (nn.Module): The model to be averaged. 18 | momentum (float): The momentum used for updating ema parameter. 19 | Ema's parameter are updated with the formula: 20 | `averaged_param = (1-momentum) * averaged_param + momentum * 21 | source_param`. Defaults to 0.0002. 22 | gamma (int): Use a larger momentum early in training and gradually 23 | annealing to a smaller value to update the ema model smoothly. The 24 | momentum is calculated as 25 | `(1 - momentum) * exp(-(1 + steps) / gamma) + momentum`. 26 | Defaults to 2000. 27 | interval (int): Interval between two updates. Defaults to 1. 28 | device (torch.device, optional): If provided, the averaged model will 29 | be stored on the :attr:`device`. Defaults to None. 30 | update_buffers (bool): if True, it will compute running averages for 31 | both the parameters and the buffers of the model. Defaults to 32 | False. 33 | """ 34 | 35 | def __init__( 36 | self, 37 | model: nn.Module, 38 | momentum: float = 0.0002, 39 | gamma: int = 2000, 40 | interval=1, 41 | device: Optional[torch.device] = None, 42 | update_buffers: bool = False, 43 | ) -> None: 44 | super().__init__( 45 | model=model, 46 | momentum=momentum, 47 | interval=interval, 48 | device=device, 49 | update_buffers=update_buffers, 50 | ) 51 | assert gamma > 0, f"gamma must be greater than 0, but got {gamma}" 52 | self.gamma = gamma 53 | 54 | def avg_func( 55 | self, averaged_param: Tensor, source_param: Tensor, steps: int 56 | ) -> None: 57 | """Compute the moving average of the parameters using the exponential 58 | momentum strategy. 59 | 60 | Args: 61 | averaged_param (Tensor): The averaged parameters. 62 | source_param (Tensor): The source parameters. 63 | steps (int): The number of times the parameters have been 64 | updated. 65 | """ 66 | momentum = (1 - self.momentum) * math.exp( 67 | -float(1 + steps) / self.gamma 68 | ) + self.momentum 69 | averaged_param.lerp_(source_param, momentum) 70 | -------------------------------------------------------------------------------- /sscma/models/losses/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .cross_entropy_loss import CrossEntropyLoss, binary_cross_entropy, cross_entropy 3 | from .gfocal_loss import QualityFocalLoss, DistributionFocalLoss 4 | from .iou_loss import ( 5 | IoULoss, 6 | BoundedIoULoss, 7 | GIoULoss, 8 | DIoULoss, 9 | CIoULoss, 10 | EIoULoss, 11 | SIoULoss, 12 | ) 13 | from .label_smooth_loss import LabelSmoothLoss 14 | from .utils import reduce_loss, weight_reduce_loss, weighted_loss, convert_to_one_hot 15 | from .pfld_loss import PFLDLoss 16 | 17 | 18 | __all__ = [ 19 | "cross_entropy", 20 | "binary_cross_entropy", 21 | "CrossEntropyLoss", 22 | "QualityFocalLoss", 23 | "DistributionFocalLoss", 24 | "IoULoss", 25 | "BoundedIoULoss", 26 | "GIoULoss", 27 | "DIoULoss", 28 | "CIoULoss", 29 | "EIoULoss", 30 | "SIoULoss", 31 | "LabelSmoothLoss", 32 | "reduce_loss", 33 | "weight_reduce_loss", 34 | "weighted_loss", 35 | "convert_to_one_hot", 36 | "PFLDLoss", 37 | ] 38 | -------------------------------------------------------------------------------- /sscma/models/losses/pfld_loss.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Seeed Technology Co.,Ltd. All rights reserved. 2 | import torch 3 | import torch.nn as nn 4 | 5 | 6 | class PFLDLoss(nn.Module): 7 | def __init__(self): 8 | super(PFLDLoss, self).__init__() 9 | 10 | def forward(self, landmarks, landmark_gt): 11 | # angle_loss = torch.sum(1-torch.cos((angle-angle_gt)),axis=0) 12 | l2_distant = torch.sum( 13 | (landmark_gt - landmarks) * (landmark_gt - landmarks), axis=1 14 | ) 15 | 16 | return torch.mean(l2_distant) 17 | -------------------------------------------------------------------------------- /sscma/models/necks/__init__.py: -------------------------------------------------------------------------------- 1 | from .cspnext_pafpn import CSPNeXtPAFPN, BaseYOLONeck 2 | from .gap import GlobalAveragePooling 3 | from .sppf import SPPFBottleneck 4 | from .fpn import FPN 5 | from .pafpn import YOLOv5PAFPN, BaseYOLONeck 6 | 7 | __all__ = [ 8 | "CSPNeXtPAFPN", 9 | "BaseYOLONeck", 10 | "GlobalAveragePooling", 11 | "SPPFBottleneck", 12 | "FPN", 13 | "YOLOv5PAFPN", 14 | "BaseYOLONeck", 15 | ] 16 | -------------------------------------------------------------------------------- /sscma/models/necks/gap.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import torch 3 | import torch.nn as nn 4 | 5 | 6 | class GlobalAveragePooling(nn.Module): 7 | """Global Average Pooling neck. 8 | 9 | Note that we use `view` to remove extra channel after pooling. We do not 10 | use `squeeze` as it will also remove the batch dimension when the tensor 11 | has a batch dimension of size 1, which can lead to unexpected errors. 12 | 13 | Args: 14 | dim (int): Dimensions of each sample channel, can be one of {1, 2, 3}. 15 | Default: 2 16 | """ 17 | 18 | def __init__(self, dim=2): 19 | super(GlobalAveragePooling, self).__init__() 20 | assert dim in [1, 2, 3], ( 21 | "GlobalAveragePooling dim only support " f"{1, 2, 3}, get {dim} instead." 22 | ) 23 | if dim == 1: 24 | self.gap = nn.AdaptiveAvgPool1d(1) 25 | elif dim == 2: 26 | self.gap = nn.AdaptiveAvgPool2d((1, 1)) 27 | else: 28 | self.gap = nn.AdaptiveAvgPool3d((1, 1, 1)) 29 | 30 | def init_weights(self): 31 | pass 32 | 33 | def forward(self, inputs): 34 | if isinstance(inputs, tuple): 35 | outs = tuple([self.gap(x) for x in inputs]) 36 | outs = tuple([out.view(x.size(0), -1) for out, x in zip(outs, inputs)]) 37 | elif isinstance(inputs, torch.Tensor): 38 | outs = self.gap(inputs) 39 | outs = outs.view(inputs.size(0), -1) 40 | else: 41 | raise TypeError("neck inputs should be tuple or torch.tensor") 42 | return outs 43 | -------------------------------------------------------------------------------- /sscma/models/necks/sppf.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Seeed Technology Co.,Ltd. 2 | # Copyright (c) OpenMMLab. 3 | from typing import Sequence, Union 4 | 5 | import torch 6 | import torch.nn as nn 7 | from torch import Tensor 8 | 9 | from mmengine.model import BaseModule 10 | from sscma.utils.typing_utils import ConfigType, OptMultiConfig 11 | from ..cnn import ConvModule 12 | 13 | 14 | class SPPFBottleneck(BaseModule): 15 | def __init__( 16 | self, 17 | in_channels: int, 18 | out_channels: int, 19 | kernel_sizes: Union[int, Sequence[int]] = 5, 20 | use_conv_first: bool = True, 21 | mid_channels_scale: float = 0.5, 22 | conv_cfg: ConfigType = None, 23 | norm_cfg: ConfigType = dict(type="BN", momentum=0.03, eps=0.001), 24 | act_cfg: ConfigType = dict(type="SiLU", inplace=True), 25 | init_cfg: OptMultiConfig = None, 26 | ): 27 | super().__init__(init_cfg) 28 | 29 | if use_conv_first: 30 | mid_channels = int(in_channels * mid_channels_scale) 31 | self.conv1 = ConvModule( 32 | in_channels, 33 | mid_channels, 34 | 1, 35 | stride=1, 36 | conv_cfg=conv_cfg, 37 | norm_cfg=norm_cfg, 38 | act_cfg=act_cfg, 39 | ) 40 | else: 41 | mid_channels = in_channels 42 | self.conv1 = None 43 | self.kernel_sizes = kernel_sizes 44 | if isinstance(kernel_sizes, int): 45 | self.poolings = nn.MaxPool2d( 46 | kernel_size=kernel_sizes, stride=1, padding=kernel_sizes // 2 47 | ) 48 | conv2_in_channels = mid_channels * 4 49 | else: 50 | self.poolings = nn.ModuleList( 51 | [ 52 | nn.MaxPool2d(kernel_size=ks, stride=1, padding=ks // 2) 53 | for ks in kernel_sizes 54 | ] 55 | ) 56 | conv2_in_channels = mid_channels * (len(kernel_sizes) + 1) 57 | 58 | self.conv2 = ConvModule( 59 | conv2_in_channels, 60 | out_channels, 61 | 1, 62 | conv_cfg=conv_cfg, 63 | norm_cfg=norm_cfg, 64 | act_cfg=act_cfg, 65 | ) 66 | 67 | def forward(self, x: Tensor) -> Tensor: 68 | if self.conv1: 69 | x = self.conv1(x) 70 | if isinstance(self.kernel_sizes, int): 71 | y1 = self.poolings(x) 72 | y2 = self.poolings(y1) 73 | x = torch.cat([x, y1, y2, self.poolings(y2)], dim=1) 74 | else: 75 | x = torch.cat([x] + [pooling(x) for pooling in self.poolings], dim=1) 76 | x = self.conv2(x) 77 | return x 78 | -------------------------------------------------------------------------------- /sscma/models/task_modules/__init__.py: -------------------------------------------------------------------------------- 1 | from .assigners import * # noqa 2 | from .coders import * # noqa 3 | from .prior_generators import * # noqa: F401,F403 4 | from .samplers import * # noqa -------------------------------------------------------------------------------- /sscma/models/task_modules/assigners/__init__.py: -------------------------------------------------------------------------------- 1 | from .assign_result import AssignResult 2 | from .base_assigner import BaseAssigner 3 | from .batch_dsl_assigner import BatchDynamicSoftLabelAssigner 4 | from .dynamic_soft_label_assigner import DynamicSoftLabelAssigner 5 | from .iou2d_calculator import BboxOverlaps2D, BboxOverlaps2D_GLIP 6 | 7 | __all__ = [ 8 | "AssignResult", 9 | "BaseAssigner", 10 | "BatchDynamicSoftLabelAssigner", 11 | "DynamicSoftLabelAssigner", 12 | "BboxOverlaps2D", 13 | "BboxOverlaps2D_GLIP", 14 | ] 15 | -------------------------------------------------------------------------------- /sscma/models/task_modules/assigners/base_assigner.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from abc import ABCMeta, abstractmethod 3 | from typing import Optional 4 | 5 | from mmengine.structures import InstanceData 6 | 7 | 8 | class BaseAssigner(metaclass=ABCMeta): 9 | """Base assigner that assigns boxes to ground truth boxes.""" 10 | 11 | @abstractmethod 12 | def assign( 13 | self, 14 | pred_instances: InstanceData, 15 | gt_instances: InstanceData, 16 | gt_instances_ignore: Optional[InstanceData] = None, 17 | **kwargs, 18 | ): 19 | """Assign boxes to either a ground truth boxes or a negative boxes.""" 20 | -------------------------------------------------------------------------------- /sscma/models/task_modules/coders/__init__.py: -------------------------------------------------------------------------------- 1 | from .base_bbox_coder import BaseBBoxCoder 2 | from .distance_point_bbox_coder import DistancePointBBoxCoder 3 | from .yolov5_bbox_coder import YOLOv5BBoxCoder 4 | 5 | __all__ = ['BaseBBoxCoder', 'DistancePointBBoxCoder', 'YOLOv5BBoxCoder'] -------------------------------------------------------------------------------- /sscma/models/task_modules/coders/base_bbox_coder.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from abc import ABCMeta, abstractmethod 3 | 4 | 5 | class BaseBBoxCoder(metaclass=ABCMeta): 6 | """Base bounding box coder. 7 | 8 | Args: 9 | use_box_type (bool): Whether to warp decoded boxes with the 10 | box type data structure. Defaults to False. 11 | """ 12 | 13 | # The size of the last of dimension of the encoded tensor. 14 | encode_size = 4 15 | 16 | def __init__(self, use_box_type: bool = False, **kwargs): 17 | self.use_box_type = use_box_type 18 | 19 | @abstractmethod 20 | def encode(self, bboxes, gt_bboxes): 21 | """Encode deltas between bboxes and ground truth boxes.""" 22 | 23 | @abstractmethod 24 | def decode(self, bboxes, bboxes_pred): 25 | """Decode the predicted bboxes according to prediction and base 26 | boxes.""" 27 | -------------------------------------------------------------------------------- /sscma/models/task_modules/coders/yolov5_bbox_coder.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from typing import Union 3 | 4 | import torch 5 | 6 | from sscma.models.task_modules.coders.base_bbox_coder import BaseBBoxCoder 7 | from mmengine.registry import TASK_UTILS 8 | 9 | 10 | @TASK_UTILS.register_module() 11 | class YOLOv5BBoxCoder(BaseBBoxCoder): 12 | """YOLOv5 BBox coder. 13 | 14 | This decoder decodes pred bboxes (delta_x, delta_x, w, h) to bboxes (tl_x, 15 | tl_y, br_x, br_y). 16 | """ 17 | 18 | def encode(self, **kwargs): 19 | """Encode deltas between bboxes and ground truth boxes.""" 20 | pass 21 | 22 | def decode( 23 | self, 24 | priors: torch.Tensor, 25 | pred_bboxes: torch.Tensor, 26 | stride: Union[torch.Tensor, int], 27 | ) -> torch.Tensor: 28 | """Decode regression results (delta_x, delta_x, w, h) to bboxes (tl_x, 29 | tl_y, br_x, br_y). 30 | 31 | Args: 32 | priors (torch.Tensor): Basic boxes or points, e.g. anchors. 33 | pred_bboxes (torch.Tensor): Encoded boxes with shape 34 | stride (torch.Tensor | int): Strides of bboxes. 35 | 36 | Returns: 37 | torch.Tensor: Decoded boxes. 38 | """ 39 | assert pred_bboxes.size(-1) == priors.size(-1) == 4 40 | 41 | pred_bboxes = pred_bboxes.sigmoid() 42 | 43 | x_center = (priors[..., 0] + priors[..., 2]) * 0.5 44 | y_center = (priors[..., 1] + priors[..., 3]) * 0.5 45 | w = priors[..., 2] - priors[..., 0] 46 | h = priors[..., 3] - priors[..., 1] 47 | 48 | # The anchor of mmdet has been offset by 0.5 49 | x_center_pred = (pred_bboxes[..., 0] - 0.5) * 2 * stride + x_center 50 | y_center_pred = (pred_bboxes[..., 1] - 0.5) * 2 * stride + y_center 51 | w_pred = (pred_bboxes[..., 2] * 2) ** 2 * w 52 | h_pred = (pred_bboxes[..., 3] * 2) ** 2 * h 53 | 54 | decoded_bboxes = torch.stack( 55 | ( 56 | x_center_pred - w_pred / 2, 57 | y_center_pred - h_pred / 2, 58 | x_center_pred + w_pred / 2, 59 | y_center_pred + h_pred / 2, 60 | ), 61 | dim=-1, 62 | ) 63 | 64 | return decoded_bboxes 65 | -------------------------------------------------------------------------------- /sscma/models/task_modules/prior_generators/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .anchor_generator import ( 3 | AnchorGenerator, 4 | LegacyAnchorGenerator, 5 | SSDAnchorGenerator, 6 | YOLOAnchorGenerator, 7 | ) 8 | from .point_generator import PointGenerator, MlvlPointGenerator 9 | from .utils import anchor_inside_flags, calc_region 10 | 11 | __all__ = [ 12 | "AnchorGenerator", 13 | "LegacyAnchorGenerator", 14 | "anchor_inside_flags", 15 | "PointGenerator", 16 | "MlvlPointGenerator", 17 | "calc_region", 18 | "YOLOAnchorGenerator", 19 | "SSDAnchorGenerator", 20 | ] 21 | -------------------------------------------------------------------------------- /sscma/models/task_modules/samplers/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .base_sampler import BaseSampler 3 | from .pseudo_sampler import PseudoSampler 4 | from .sampling_result import SamplingResult 5 | 6 | __all__ = ["BaseSampler", "PseudoSampler", "SamplingResult"] 7 | -------------------------------------------------------------------------------- /sscma/models/task_modules/samplers/pseudo_sampler.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import torch 3 | 4 | from mmengine.structures import InstanceData 5 | 6 | from ..assigners.assign_result import AssignResult 7 | from .base_sampler import BaseSampler 8 | from .sampling_result import SamplingResult 9 | 10 | 11 | class PseudoSampler(BaseSampler): 12 | """A pseudo sampler that does not do sampling actually.""" 13 | 14 | def __init__(self, **kwargs): 15 | pass 16 | 17 | def _sample_pos(self, **kwargs): 18 | """Sample positive samples.""" 19 | raise NotImplementedError 20 | 21 | def _sample_neg(self, **kwargs): 22 | """Sample negative samples.""" 23 | raise NotImplementedError 24 | 25 | def sample( 26 | self, 27 | assign_result: AssignResult, 28 | pred_instances: InstanceData, 29 | gt_instances: InstanceData, 30 | *args, 31 | **kwargs, 32 | ): 33 | """Directly returns the positive and negative indices of samples. 34 | 35 | Args: 36 | assign_result (:obj:`AssignResult`): Bbox assigning results. 37 | pred_instances (:obj:`InstanceData`): Instances of model 38 | predictions. It includes ``priors``, and the priors can 39 | be anchors, points, or bboxes predicted by the model, 40 | shape(n, 4). 41 | gt_instances (:obj:`InstanceData`): Ground truth of instance 42 | annotations. It usually includes ``bboxes`` and ``labels`` 43 | attributes. 44 | 45 | Returns: 46 | :obj:`SamplingResult`: sampler results 47 | """ 48 | gt_bboxes = gt_instances.bboxes 49 | priors = pred_instances.priors 50 | 51 | pos_inds = ( 52 | torch.nonzero(assign_result.gt_inds > 0, as_tuple=False) 53 | .squeeze(-1) 54 | .unique() 55 | ) 56 | neg_inds = ( 57 | torch.nonzero(assign_result.gt_inds == 0, as_tuple=False) 58 | .squeeze(-1) 59 | .unique() 60 | ) 61 | 62 | gt_flags = priors.new_zeros(priors.shape[0], dtype=torch.uint8) 63 | sampling_result = SamplingResult( 64 | pos_inds=pos_inds, 65 | neg_inds=neg_inds, 66 | priors=priors, 67 | gt_bboxes=gt_bboxes, 68 | assign_result=assign_result, 69 | gt_flags=gt_flags, 70 | avg_factor_with_neg=False, 71 | ) 72 | return sampling_result 73 | -------------------------------------------------------------------------------- /sscma/models/test_time_augs/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .det_tta import DetTTAModel 3 | from .merge_augs import ( 4 | merge_aug_bboxes, 5 | merge_aug_masks, 6 | merge_aug_proposals, 7 | merge_aug_results, 8 | merge_aug_scores, 9 | ) 10 | 11 | __all__ = [ 12 | "merge_aug_bboxes", 13 | "merge_aug_masks", 14 | "merge_aug_proposals", 15 | "merge_aug_scores", 16 | "merge_aug_results", 17 | "DetTTAModel", 18 | ] 19 | -------------------------------------------------------------------------------- /sscma/models/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .batch_augments import Mixup, CutMix 2 | from .data_processor import ClsDataPreprocessor 3 | from .misc import ( 4 | make_divisible, 5 | _make_divisible, 6 | make_round, 7 | auto_arrange_images, 8 | get_file_list, 9 | IMG_EXTENSIONS, 10 | ) 11 | 12 | __all__ = [ 13 | "Mixup", 14 | "CutMix", 15 | "ClsDataPreprocessor", 16 | "make_divisible", 17 | "_make_divisible", 18 | "make_round", 19 | "auto_arrange_images", 20 | "get_file_list", 21 | "IMG_EXTENSIONS", 22 | ] 23 | -------------------------------------------------------------------------------- /sscma/optim/__init__.py: -------------------------------------------------------------------------------- 1 | from .optimizer import * 2 | 3 | -------------------------------------------------------------------------------- /sscma/optim/optimizer/__init__.py: -------------------------------------------------------------------------------- 1 | from .lamb import Lamb 2 | 3 | __all__ = ["Lamb"] 4 | -------------------------------------------------------------------------------- /sscma/quantizer/__init__.py: -------------------------------------------------------------------------------- 1 | from .models import * # noqa: F403 2 | -------------------------------------------------------------------------------- /sscma/quantizer/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .rtmdet_quantizer import RtmdetQuantModel 2 | from .pfld_quantizer import PFLDQuantModel 3 | from .fomo_quantizer import FomoQuantizer 4 | from .anomaly_quantizer import AnomalyQuantModel 5 | 6 | __all__ = ["RtmdetQuantModel", "PFLDQuantModel", "FomoQuantizer", "AnomalyQuantModel"] 7 | -------------------------------------------------------------------------------- /sscma/quantizer/models/pfld_quantizer.py: -------------------------------------------------------------------------------- 1 | from typing import Union, List, Dict, Tuple 2 | import torch 3 | import numpy as np 4 | 5 | from mmengine.registry import MODELS 6 | from mmengine.model import BaseModel 7 | from sscma.utils.typing_utils import OptConfigType, OptMultiConfig 8 | from sscma.structures import DetDataSample, OptSampleList 9 | from sscma.models.heads.pfld_head import pose_acc 10 | from sscma.structures import PoseDataSample 11 | from mmengine.structures.instance_data import InstanceData 12 | 13 | ForwardResults = Union[ 14 | Dict[str, torch.Tensor], List[DetDataSample], Tuple[torch.Tensor], torch.Tensor 15 | ] 16 | 17 | 18 | class PFLDQuantModel(BaseModel): 19 | def __init__( 20 | self, 21 | data_preprocessor: OptConfigType = None, 22 | init_cfg: OptMultiConfig = None, 23 | tinynn_model: torch.nn.Module = None, 24 | loss_cfg: dict = dict(type="PFLDLoss"), 25 | ): 26 | super().__init__(data_preprocessor=data_preprocessor, init_cfg=init_cfg) 27 | self._model = tinynn_model 28 | self.lossFunction = MODELS.build(loss_cfg) 29 | # self.bbox_head = bbox_head 30 | 31 | def forward( 32 | self, inputs: torch.Tensor, data_samples: OptSampleList, mode: str = "predict" 33 | ): 34 | if mode == "predict": 35 | return self.predict(inputs, data_samples) 36 | elif mode == "loss": 37 | return self.loss(inputs, data_samples) 38 | 39 | def loss(self, inputs: torch.Tensor, data_samples: OptSampleList): 40 | preds = self._model(inputs) 41 | labels = torch.as_tensor( 42 | data_samples["keypoints"], device=preds.device, dtype=torch.float32 43 | ) 44 | loss = self.lossFunction(preds, labels) 45 | acc = pose_acc(preds, labels, data_samples["hw"]) 46 | return {"loss": loss, "Acc": torch.as_tensor(acc, dtype=torch.float32)} 47 | 48 | def predict(self, inputs: torch.Tensor, data_samples: OptSampleList): 49 | data = self._model(inputs) 50 | res = PoseDataSample(**data_samples) 51 | res.results = data 52 | res.pred_instances = InstanceData( 53 | keypoints=np.array([data.reshape(-1, 2).cpu().numpy()]) 54 | * data_samples["init_size"][1].reshape(-1, 1).cpu().numpy() 55 | ) 56 | 57 | return [res] 58 | 59 | def set_model(self, model): 60 | self._model = model 61 | -------------------------------------------------------------------------------- /sscma/structures/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .data_sample import ( 3 | DataSample, 4 | DetDataSample, 5 | MultiTaskDataSample, 6 | OptSampleList, 7 | SampleList, 8 | PoseDataSample, 9 | ) 10 | from .utils import ( 11 | batch_label_to_onehot, 12 | cat_batch_labels, 13 | format_label, 14 | format_score, 15 | label_to_onehot, 16 | tensor_split, 17 | ) 18 | 19 | __all__ = [ 20 | "OptSampleList", 21 | "SampleList", 22 | "DataSample", 23 | "DetDataSample", 24 | "batch_label_to_onehot", 25 | "cat_batch_labels", 26 | "tensor_split", 27 | "MultiTaskDataSample", 28 | "label_to_onehot", 29 | "format_label", 30 | "format_score", 31 | "PoseDataSample", 32 | ] 33 | -------------------------------------------------------------------------------- /sscma/structures/bbox/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .base_boxes import BaseBoxes 3 | from .bbox_overlaps import bbox_overlaps 4 | from .box_type import ( 5 | autocast_box_type, 6 | convert_box_type, 7 | get_box_type, 8 | register_box, 9 | register_box_converter, 10 | ) 11 | from .horizontal_boxes import HorizontalBoxes 12 | from .transforms import bbox_cxcyah_to_xyxy # noqa: E501 13 | from .transforms import ( 14 | bbox2corner, 15 | bbox2distance, 16 | bbox2result, 17 | bbox2roi, 18 | bbox_cxcywh_to_xyxy, 19 | bbox_flip, 20 | bbox_mapping, 21 | bbox_mapping_back, 22 | bbox_project, 23 | bbox_rescale, 24 | bbox_xyxy_to_cxcyah, 25 | bbox_xyxy_to_cxcywh, 26 | cat_boxes, 27 | corner2bbox, 28 | distance2bbox, 29 | empty_box_as, 30 | find_inside_bboxes, 31 | get_box_tensor, 32 | get_box_wh, 33 | roi2bbox, 34 | scale_boxes, 35 | stack_boxes, 36 | ) 37 | 38 | __all__ = [ 39 | "bbox_overlaps", 40 | "bbox_flip", 41 | "bbox_mapping", 42 | "bbox_mapping_back", 43 | "bbox2roi", 44 | "roi2bbox", 45 | "bbox2result", 46 | "distance2bbox", 47 | "bbox2distance", 48 | "bbox_rescale", 49 | "bbox_cxcywh_to_xyxy", 50 | "bbox_xyxy_to_cxcywh", 51 | "find_inside_bboxes", 52 | "bbox2corner", 53 | "corner2bbox", 54 | "bbox_project", 55 | "BaseBoxes", 56 | "convert_box_type", 57 | "get_box_type", 58 | "register_box", 59 | "register_box_converter", 60 | "HorizontalBoxes", 61 | "autocast_box_type", 62 | "cat_boxes", 63 | "stack_boxes", 64 | "scale_boxes", 65 | "get_box_wh", 66 | "get_box_tensor", 67 | "empty_box_as", 68 | "bbox_xyxy_to_cxcyah", 69 | "bbox_cxcyah_to_xyxy", 70 | ] 71 | -------------------------------------------------------------------------------- /sscma/structures/mask/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .mask_target import mask_target 3 | from .structures import ( 4 | BaseInstanceMasks, 5 | BitmapMasks, 6 | PolygonMasks, 7 | bitmap_to_polygon, 8 | polygon_to_bitmap, 9 | ) 10 | from .utils import encode_mask_results, mask2bbox, split_combined_polys 11 | 12 | __all__ = [ 13 | "split_combined_polys", 14 | "mask_target", 15 | "BaseInstanceMasks", 16 | "BitmapMasks", 17 | "PolygonMasks", 18 | "encode_mask_results", 19 | "mask2bbox", 20 | "polygon_to_bitmap", 21 | "bitmap_to_polygon", 22 | ] 23 | -------------------------------------------------------------------------------- /sscma/structures/mask/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import numpy as np 3 | import pycocotools.mask as mask_util 4 | import torch 5 | from mmengine.utils import slice_list 6 | 7 | 8 | def split_combined_polys(polys, poly_lens, polys_per_mask): 9 | """Split the combined 1-D polys into masks. 10 | 11 | A mask is represented as a list of polys, and a poly is represented as 12 | a 1-D array. In dataset, all masks are concatenated into a single 1-D 13 | tensor. Here we need to split the tensor into original representations. 14 | 15 | Args: 16 | polys (list): a list (length = image num) of 1-D tensors 17 | poly_lens (list): a list (length = image num) of poly length 18 | polys_per_mask (list): a list (length = image num) of poly number 19 | of each mask 20 | 21 | Returns: 22 | list: a list (length = image num) of list (length = mask num) of \ 23 | list (length = poly num) of numpy array. 24 | """ 25 | mask_polys_list = [] 26 | for img_id in range(len(polys)): 27 | polys_single = polys[img_id] 28 | polys_lens_single = poly_lens[img_id].tolist() 29 | polys_per_mask_single = polys_per_mask[img_id].tolist() 30 | 31 | split_polys = slice_list(polys_single, polys_lens_single) 32 | mask_polys = slice_list(split_polys, polys_per_mask_single) 33 | mask_polys_list.append(mask_polys) 34 | return mask_polys_list 35 | 36 | 37 | # TODO: move this function to more proper place 38 | def encode_mask_results(mask_results): 39 | """Encode bitmap mask to RLE code. 40 | 41 | Args: 42 | mask_results (list): bitmap mask results. 43 | 44 | Returns: 45 | list | tuple: RLE encoded mask. 46 | """ 47 | encoded_mask_results = [] 48 | for mask in mask_results: 49 | encoded_mask_results.append( 50 | mask_util.encode( 51 | np.array(mask[:, :, np.newaxis], order="F", dtype="uint8") 52 | )[0] 53 | ) # encoded with RLE 54 | return encoded_mask_results 55 | 56 | 57 | def mask2bbox(masks): 58 | """Obtain tight bounding boxes of binary masks. 59 | 60 | Args: 61 | masks (Tensor): Binary mask of shape (n, h, w). 62 | 63 | Returns: 64 | Tensor: Bboxe with shape (n, 4) of \ 65 | positive region in binary mask. 66 | """ 67 | N = masks.shape[0] 68 | bboxes = masks.new_zeros((N, 4), dtype=torch.float32) 69 | x_any = torch.any(masks, dim=1) 70 | y_any = torch.any(masks, dim=2) 71 | for i in range(N): 72 | x = torch.where(x_any[i, :])[0] 73 | y = torch.where(y_any[i, :])[0] 74 | if len(x) > 0 and len(y) > 0: 75 | bboxes[i, :] = bboxes.new_tensor([x[0], y[0], x[-1] + 1, y[-1] + 1]) 76 | 77 | return bboxes 78 | -------------------------------------------------------------------------------- /sscma/utils/__init__.py: -------------------------------------------------------------------------------- 1 | from .simplecv import ( 2 | _scale_size, 3 | simplecv_imresize, 4 | simplecv_imflip, 5 | simplecv_imcrop, 6 | simplecv_imrescale, 7 | simplecv_imread, 8 | simplecv_imfrombytes, 9 | simplecv_imwrite, 10 | simplecv_rescale_size, 11 | simplecv_impad, 12 | simplecv_imtranslate, 13 | simplecv_imshear, 14 | simplecv_imrotate, 15 | simplecv_color_val, 16 | ) 17 | 18 | from .colorspace import ( 19 | simplecv_bgr2gray, 20 | simplecv_bgr2hls, 21 | simplecv_bgr2hsv, 22 | simplecv_bgr2rgb, 23 | simplecv_bgr2ycbcr, 24 | simplecv_gray2bgr, 25 | simplecv_gray2rgb, 26 | simplecv_hls2bgr, 27 | simplecv_hsv2bgr, 28 | simplecv_imconvert, 29 | simplecv_rgb2bgr, 30 | simplecv_rgb2gray, 31 | simplecv_rgb2ycbcr, 32 | simplecv_ycbcr2bgr, 33 | simplecv_ycbcr2rgb, 34 | ) 35 | 36 | from .logger import get_caller_name, log_img_scale 37 | from .typing_utils import ( 38 | ConfigType, 39 | OptConfigType, 40 | MultiConfig, 41 | OptMultiConfig, 42 | InstanceList, 43 | OptInstanceList, 44 | PixelList, 45 | OptPixelList, 46 | RangeType, 47 | ) 48 | 49 | from .lazy_import import lazy_import 50 | 51 | __all__ = [ 52 | "_scale_size", 53 | "simplecv_imresize", 54 | "simplecv_imflip", 55 | "simplecv_imcrop", 56 | "simplecv_imrescale", 57 | "simplecv_imread", 58 | "simplecv_imfrombytes", 59 | "simplecv_imwrite", 60 | "simplecv_rescale_size", 61 | "simplecv_impad", 62 | "simplecv_imtranslate", 63 | "simplecv_imshear", 64 | "simplecv_imrotate", 65 | "simplecv_color_val", 66 | "simplecv_bgr2gray", 67 | "simplecv_bgr2hls", 68 | "simplecv_bgr2hsv", 69 | "simplecv_bgr2rgb", 70 | "simplecv_bgr2ycbcr", 71 | "simplecv_gray2bgr", 72 | "simplecv_gray2rgb", 73 | "simplecv_hls2bgr", 74 | "simplecv_hsv2bgr", 75 | "simplecv_imconvert", 76 | "simplecv_rgb2bgr", 77 | "simplecv_rgb2gray", 78 | "simplecv_rgb2ycbcr", 79 | "simplecv_ycbcr2bgr", 80 | "simplecv_ycbcr2rgb", 81 | "get_caller_name", 82 | "log_img_scale", 83 | "ConfigType", 84 | "OptConfigType", 85 | "MultiConfig", 86 | "OptMultiConfig", 87 | "InstanceList", 88 | "OptInstanceList", 89 | "PixelList", 90 | "OptPixelList", 91 | "RangeType", 92 | "lazy_import" 93 | ] 94 | -------------------------------------------------------------------------------- /sscma/utils/logger.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | import inspect 3 | 4 | from mmengine.logging import print_log 5 | 6 | 7 | def get_caller_name(): 8 | """Get name of caller method.""" 9 | # this_func_frame = inspect.stack()[0][0] # i.e., get_caller_name 10 | # callee_frame = inspect.stack()[1][0] # e.g., log_img_scale 11 | caller_frame = inspect.stack()[2][0] # e.g., caller of log_img_scale 12 | caller_method = caller_frame.f_code.co_name 13 | try: 14 | caller_class = caller_frame.f_locals["self"].__class__.__name__ 15 | return f"{caller_class}.{caller_method}" 16 | except KeyError: # caller is a function 17 | return caller_method 18 | 19 | 20 | def log_img_scale(img_scale, shape_order="hw", skip_square=False): 21 | """Log image size. 22 | 23 | Args: 24 | img_scale (tuple): Image size to be logged. 25 | shape_order (str, optional): The order of image shape. 26 | 'hw' for (height, width) and 'wh' for (width, height). 27 | Defaults to 'hw'. 28 | skip_square (bool, optional): Whether to skip logging for square 29 | img_scale. Defaults to False. 30 | 31 | Returns: 32 | bool: Whether to have done logging. 33 | """ 34 | if shape_order == "hw": 35 | height, width = img_scale 36 | elif shape_order == "wh": 37 | width, height = img_scale 38 | else: 39 | raise ValueError(f"Invalid shape_order {shape_order}.") 40 | 41 | if skip_square and (height == width): 42 | return False 43 | 44 | caller = get_caller_name() 45 | print_log( 46 | f"image shape: height={height}, width={width} in {caller}", logger="current" 47 | ) 48 | 49 | return True 50 | -------------------------------------------------------------------------------- /sscma/utils/typing_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | """Collecting some commonly used type hint in mmdetection.""" 3 | 4 | from typing import List, Optional, Sequence, Tuple, Union 5 | 6 | from mmengine.config import ConfigDict 7 | from mmengine.structures import InstanceData, PixelData 8 | 9 | # TODO: Need to avoid circular import with assigner and sampler 10 | # Type hint of config data 11 | ConfigType = Union[ConfigDict, dict] 12 | OptConfigType = Optional[ConfigType] 13 | # Type hint of one or more config data 14 | MultiConfig = Union[ConfigType, List[ConfigType]] 15 | OptMultiConfig = Optional[MultiConfig] 16 | 17 | InstanceList = List[InstanceData] 18 | OptInstanceList = Optional[InstanceList] 19 | 20 | PixelList = List[PixelData] 21 | OptPixelList = Optional[PixelList] 22 | 23 | RangeType = Sequence[Tuple[int, int]] 24 | -------------------------------------------------------------------------------- /sscma/utils/util_random.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | """Helpers for random number generators.""" 3 | 4 | import numpy as np 5 | 6 | 7 | def ensure_rng(rng=None): 8 | """Coerces input into a random number generator. 9 | 10 | If the input is None, then a global random state is returned. 11 | 12 | If the input is a numeric value, then that is used as a seed to construct a 13 | random state. Otherwise the input is returned as-is. 14 | 15 | Adapted from [1]_. 16 | 17 | Args: 18 | rng (int | numpy.random.RandomState | None): 19 | if None, then defaults to the global rng. Otherwise this can be an 20 | integer or a RandomState class 21 | Returns: 22 | (numpy.random.RandomState) : rng - 23 | a numpy random number generator 24 | 25 | References: 26 | .. [1] https://gitlab.kitware.com/computer-vision/kwarray/blob/master/kwarray/util_random.py#L270 # noqa: E501 27 | """ 28 | 29 | if rng is None: 30 | rng = np.random.mtrand._rand 31 | elif isinstance(rng, int): 32 | rng = np.random.RandomState(rng) 33 | else: 34 | rng = rng 35 | return rng 36 | -------------------------------------------------------------------------------- /sscma/version.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.1.0" 2 | short_version = __version__ 3 | 4 | 5 | def parse_version_info(version_str): 6 | """Parse a version string into a tuple. 7 | 8 | Args: 9 | version_str (str): The version string. 10 | Returns: 11 | tuple[int or str]: The version info, e.g., "1.3.0" is parsed into 12 | (1, 3, 0), and "2.0.0rc1" is parsed into (2, 0, 0, 'rc1'). 13 | """ 14 | version_info = [] 15 | for x in version_str.split("."): 16 | if x.isdigit(): 17 | version_info.append(int(x)) 18 | elif x.find("rc") != -1: 19 | patch_version = x.split("rc") 20 | version_info.append(int(patch_version[0])) 21 | version_info.append(f"rc{patch_version[1]}") 22 | return tuple(version_info) 23 | 24 | 25 | version_info = parse_version_info(__version__) 26 | -------------------------------------------------------------------------------- /sscma/visualization/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) OpenMMLab. All rights reserved. 2 | from .local_visualizer import DetLocalVisualizer, random_color, PoseLocalVisualizer 3 | from .palette import palette_val, get_palette, _get_adaptive_scales, jitter_color 4 | from .visualizer import UniversalVisualizer, FomoLocalVisualizer 5 | 6 | __all__ = [ 7 | "DetLocalVisualizer", 8 | "random_color", 9 | "palette_val", 10 | "get_palette", 11 | "_get_adaptive_scales", 12 | "jitter_color", 13 | "UniversalVisualizer", 14 | "FomoLocalVisualizer", 15 | "PoseLocalVisualizer", 16 | ] 17 | -------------------------------------------------------------------------------- /tools/dataset_tool/read_serial.py: -------------------------------------------------------------------------------- 1 | import serial 2 | import argparse 3 | import csv 4 | 5 | 6 | def get_args(): 7 | parser = argparse.ArgumentParser() 8 | 9 | parser.add_argument("--sample_rate", "-sr", type=int, default=115200) 10 | parser.add_argument("--port", "-p", type=str, default="COM5") 11 | parser.add_argument("--file_path", "-f", type=str, default="serial_data.csv") 12 | args = parser.parse_args() 13 | return args 14 | 15 | 16 | def main(): 17 | args = get_args() 18 | port, sample_rate, file_path = args.port, args.sample_rate, args.file_path 19 | ser = serial.Serial(port, sample_rate) 20 | if ser.isOpen(): 21 | print(f"Opening serial port {port} succeeded.") 22 | print(ser.name) 23 | else: 24 | print(f"Opening serial port {port} failed.") 25 | 26 | csv_file = open(file_path, "w", newline="") 27 | csv_writer = csv.writer(csv_file) 28 | with open(file_path, "a") as file: 29 | try: 30 | data = [] 31 | record = 0 32 | if sample_rate is not None: 33 | for index, i in enumerate(ser): 34 | 35 | if (index + 1) % sample_rate != 0: 36 | temp = i.decode().strip("\r\n").split(" ")[:-1] 37 | data = data + temp 38 | 39 | continue 40 | record = record + 1 41 | temp = i.decode().strip("\r\n").split(" ")[:-1] 42 | data = data + temp 43 | 44 | csv_writer.writerow(data) 45 | print("Received:", record) 46 | data = [] 47 | 48 | else: 49 | # ser.reset_input_buffer() 50 | ser.flushInput() 51 | for index, i in enumerate(ser): 52 | if index == 0: 53 | continue 54 | temp = i.decode().strip("\r\n").split(" ")[:-1] 55 | csv_writer.writerow(temp) 56 | print("Received:", index) 57 | ser.flushInput() 58 | 59 | except KeyboardInterrupt: 60 | ser.close() 61 | csv_file.close() 62 | print("Data saved to:", file_path) 63 | -------------------------------------------------------------------------------- /tools/dist_test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CONFIG=$1 4 | CHECKPOINT=$2 5 | GPUS=$3 6 | NNODES=${NNODES:-1} 7 | NODE_RANK=${NODE_RANK:-0} 8 | PORT=${PORT:-29500} 9 | MASTER_ADDR=${MASTER_ADDR:-"127.0.0.1"} 10 | 11 | PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ 12 | python -m torch.distributed.launch \ 13 | --nnodes=$NNODES \ 14 | --node_rank=$NODE_RANK \ 15 | --master_addr=$MASTER_ADDR \ 16 | --nproc_per_node=$GPUS \ 17 | --master_port=$PORT \ 18 | $(dirname "$0")/test.py \ 19 | $CONFIG \ 20 | $CHECKPOINT \ 21 | --launcher pytorch \ 22 | ${@:4} 23 | -------------------------------------------------------------------------------- /tools/dist_train.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | CONFIG=$1 4 | GPUS=$2 5 | NNODES=${NNODES:-1} 6 | NODE_RANK=${NODE_RANK:-0} 7 | PORT=${PORT:-29500} 8 | MASTER_ADDR=${MASTER_ADDR:-"127.0.0.1"} 9 | 10 | PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ 11 | python -m torch.distributed.launch \ 12 | --nnodes=$NNODES \ 13 | --node_rank=$NODE_RANK \ 14 | --master_addr=$MASTER_ADDR \ 15 | --nproc_per_node=$GPUS \ 16 | --master_port=$PORT \ 17 | $(dirname "$0")/train.py \ 18 | $CONFIG \ 19 | --launcher pytorch ${@:3} 20 | -------------------------------------------------------------------------------- /tools/slurm_test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -x 4 | 5 | PARTITION=$1 6 | JOB_NAME=$2 7 | CONFIG=$3 8 | CHECKPOINT=$4 9 | GPUS=${GPUS:-8} 10 | GPUS_PER_NODE=${GPUS_PER_NODE:-8} 11 | CPUS_PER_TASK=${CPUS_PER_TASK:-5} 12 | PY_ARGS=${@:5} 13 | SRUN_ARGS=${SRUN_ARGS:-""} 14 | 15 | PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ 16 | srun -p ${PARTITION} \ 17 | --job-name=${JOB_NAME} \ 18 | --gres=gpu:${GPUS_PER_NODE} \ 19 | --ntasks=${GPUS} \ 20 | --ntasks-per-node=${GPUS_PER_NODE} \ 21 | --cpus-per-task=${CPUS_PER_TASK} \ 22 | --kill-on-bad-exit=1 \ 23 | ${SRUN_ARGS} \ 24 | python -u tools/test.py ${CONFIG} ${CHECKPOINT} --launcher="slurm" ${PY_ARGS} 25 | -------------------------------------------------------------------------------- /tools/slurm_train.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -x 4 | 5 | PARTITION=$1 6 | JOB_NAME=$2 7 | CONFIG=$3 8 | WORK_DIR=$4 9 | GPUS=${GPUS:-8} 10 | GPUS_PER_NODE=${GPUS_PER_NODE:-8} 11 | CPUS_PER_TASK=${CPUS_PER_TASK:-5} 12 | SRUN_ARGS=${SRUN_ARGS:-""} 13 | PY_ARGS=${@:5} 14 | 15 | PYTHONPATH="$(dirname $0)/..":$PYTHONPATH \ 16 | srun -p ${PARTITION} \ 17 | --job-name=${JOB_NAME} \ 18 | --gres=gpu:${GPUS_PER_NODE} \ 19 | --ntasks=${GPUS} \ 20 | --ntasks-per-node=${GPUS_PER_NODE} \ 21 | --cpus-per-task=${CPUS_PER_TASK} \ 22 | --kill-on-bad-exit=1 \ 23 | ${SRUN_ARGS} \ 24 | python -u tools/train.py ${CONFIG} --work-dir=${WORK_DIR} --launcher="slurm" ${PY_ARGS} 25 | -------------------------------------------------------------------------------- /tools/vela_config.ini: -------------------------------------------------------------------------------- 1 | ; file: my_vela_cfg.ini ; ----------------------------------------------------------------------------- 2 | ; Vela configuration file ; ----------------------------------------------------------------------------- 3 | ; System Configuration 4 | 5 | ; My_Sys_Cfg 6 | [System_Config.My_Sys_Cfg] 7 | core_clock=400e6 8 | axi0_port=Sram 9 | axi1_port=OffChipFlash 10 | Sram_clock_scale=1.0 11 | Sram_burst_length=32 12 | Sram_read_latency=16 13 | Sram_write_latency=16 14 | Dram_clock_scale=0.75 15 | Dram_burst_length=128 16 | Dram_read_latency=500 17 | Dram_write_latency=250 18 | OnChipFlash_clock_scale=0.25 19 | OffChipFlash_clock_scale=0.015625 20 | OffChipFlash_burst_length=32 21 | OffChipFlash_read_latency=64 22 | OffChipFlash_write_latency=64 23 | ; ----------------------------------------------------------------------------- 24 | ; Memory Mode 25 | ; My_Mem_Mode_Parent 26 | [Memory_Mode.My_Mem_Mode_Parent] 27 | const_mem_area=Axi1 28 | arena_mem_area=Axi0 29 | cache_mem_area=Axi0 30 | --------------------------------------------------------------------------------