Skip to content

Commit

Permalink
Merge branch 'master' into advanced-rewriter
Browse files Browse the repository at this point in the history
  • Loading branch information
RunningLeon authored Feb 28, 2022
2 parents 5b1b0ee + ba5351e commit 0f12f50
Show file tree
Hide file tree
Showing 279 changed files with 4,926 additions and 1,725 deletions.
9 changes: 6 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ jobs:
run: rm -rf .eggs && pip install -e .
- name: Run unittests and generate coverage report
run: |
coverage run --branch --source mmdeploy -m pytest -rsE tests/
coverage run --branch --source mmdeploy -m pytest -rsE tests --ignore=tests/test_codebase/test_mmpose
coverage run --branch --source mmdeploy --append -m pytest -rsE tests/test_codebase/test_mmpose
coverage xml
coverage report -m
Expand Down Expand Up @@ -95,7 +96,8 @@ jobs:
python tools/check_env.py
- name: Run unittests and generate coverage report
run: |
coverage run --branch --source mmdeploy -m pytest -rsE tests/
coverage run --branch --source mmdeploy -m pytest -rsE tests --ignore=tests/test_codebase/test_mmpose
coverage run --branch --source mmdeploy --append -m pytest -rsE tests/test_codebase/test_mmpose
coverage xml
coverage report -m
Expand Down Expand Up @@ -139,7 +141,8 @@ jobs:
python tools/check_env.py
- name: Run unittests and generate coverage report
run: |
coverage run --branch --source mmdeploy -m pytest -rsE tests/
coverage run --branch --source mmdeploy -m pytest -rsE tests --ignore=tests/test_codebase/test_mmpose
coverage run --branch --source mmdeploy --append -m pytest -rsE tests/test_codebase/test_mmpose
coverage xml
coverage report -m
- name: Upload coverage to Codecov
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,7 @@ work_dirs/

# the generated header files
/tests/test_csrc/test_define.h

#
!docs/zh_cn/build
!docs/en/build
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,9 @@ repos:
hooks:
- id: docformatter
args: ["--in-place", "--wrap-descriptions", "79"]

- repo: https://github.com/open-mmlab/pre-commit-hooks
rev: v0.2.0
hooks:
- id: check-copyright
args: ["csrc", "mmdeploy", "tests", "demo", "tools"]
51 changes: 24 additions & 27 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ set(CMAKE_CXX_STANDARD 17)


set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
if (MSVC)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
else ()
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
endif ()
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

# options
Expand Down Expand Up @@ -39,52 +43,43 @@ endif ()
# notice that ubsan has linker issues for ubuntu < 18.04, see
# https://stackoverflow.com/questions/50024731/ld-unrecognized-option-push-state-no-as-needed
if (MMDEPLOY_UBSAN_ENABLE)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fsanitize=undefined>)
add_link_options(-fsanitize=undefined)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fsanitize=undefined>)
add_link_options(-fsanitize=undefined)
endif ()

if (MSVC)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/diagnostics:classic>)
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/Zc:preprocessor>) # /experimental:preprocessor on VS2017
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/wd4251>)
else ()
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fvisibility=hidden>)
endif ()

include(${CMAKE_SOURCE_DIR}/cmake/common.cmake)
# set INTERFACE target to gather linked modules
include(${CMAKE_SOURCE_DIR}/cmake/MMDeploy.cmake)

add_library(MMDeployStaticModules INTERFACE)
add_library(MMDeployDynamicModules INTERFACE)
add_library(MMDeployLibs INTERFACE)

add_subdirectory(csrc)

if (MMDEPLOY_BUILD_SDK)
# get static modules and dynamic modules from ${MMDeployStaticModules} and ${MMDeployDynamicModules}, respectively
set(STATIC_MODULES)
get_target_property(STATIC_MODULES MMDeployStaticModules INTERFACE_LINK_LIBRARIES)
get_target_list("${STATIC_MODULES}" FILTERED_MODULES)
set(MMDEPLOY_STATIC_MODULES "${FILTERED_MODULES}" CACHE STRING "MMDeploy's static modules")
message(STATUS "MMDEPLOY_STATIC_MODULES: ${MMDEPLOY_STATIC_MODULES}")

set(DYNAMIC_MODULES)
get_target_property(DYNAMIC_MODULES MMDeployDynamicModules INTERFACE_LINK_LIBRARIES)
get_target_list("${DYNAMIC_MODULES}" FILTERED_MODULES)
set(MMDEPLOY_DYNAMIC_MODULES "${FILTERED_MODULES}" CACHE STRING "MMDeploy's dynamic modules")
message(STATUS "MMDEPLOY_DYNAMIC_MODULES: ${MMDEPLOY_DYNAMIC_MODULES}")

# get libs from ${MMDeployLibs}
set(LIBS)
get_target_property(LIBS MMDeployLibs INTERFACE_LINK_LIBRARIES)
get_target_list("${LIBS}" FILTERED_LIBS)
set(MMDEPLOY_LIBS "${FILTERED_LIBS}" CACHE STRING "MMDeploy's libs that can be linked directly by application")
message(STATUS "MMDEPLOY_LIBS: ${MMDEPLOY_LIBS}")
install(TARGETS MMDeployStaticModules
MMDeployDynamicModules
MMDeployLibs
EXPORT MMDeployTargets)

if (MMDEPLOY_BUILD_TEST)
add_subdirectory(tests/test_csrc)
endif ()

if (MMDEPLOY_BUILD_SDK_PYTHON_API)
add_subdirectory(csrc/apis/python)
endif()
endif ()

# export MMDeploy package
install(EXPORT MMDeployTargets
# NAMESPACE mmdeploy::
FILE MMDeployTargets.cmake
#EXPORT_LINK_INTERFACE_LIBRARIES
DESTINATION lib/cmake/MMDeploy)

include(CMakePackageConfigHelpers)
Expand All @@ -105,6 +100,8 @@ if (MMDEPLOY_BUILD_SDK)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/MMDeployConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/MMDeployConfigVersion.cmake
${CMAKE_CURRENT_SOURCE_DIR}/cmake/MMDeploy.cmake
${CMAKE_CURRENT_SOURCE_DIR}/cmake/loader.cpp.in
DESTINATION lib/cmake/MMDeploy
)

Expand Down
20 changes: 11 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ a part of the [OpenMMLab](https://openmmlab.com/) project.
- [x] MMSegmentation
- [x] MMEditing
- [x] MMOCR
- [x] MMPose

- **Multiple inference backends are available**

Expand Down Expand Up @@ -111,20 +112,21 @@ If you find this project useful in your research, please consider cite:
## Projects in OpenMMLab

- [MMCV](https://github.com/open-mmlab/mmcv): OpenMMLab foundational library for computer vision.
- [MIM](https://github.com/open-mmlab/mim): MIM Installs OpenMMLab Packages.
- [MIM](https://github.com/open-mmlab/mim): MIM installs OpenMMLab packages.
- [MMClassification](https://github.com/open-mmlab/mmclassification): OpenMMLab image classification toolbox and benchmark.
- [MMDetection](https://github.com/open-mmlab/mmdetection): OpenMMLab detection toolbox and benchmark.
- [MMDetection3D](https://github.com/open-mmlab/mmdetection3d): OpenMMLab's next-generation platform for general 3D object detection.
- [MMRotate](https://github.com/open-mmlab/mmrotate): OpenMMLab rotated object detection toolbox and benchmark.
- [MMSegmentation](https://github.com/open-mmlab/mmsegmentation): OpenMMLab semantic segmentation toolbox and benchmark.
- [MMOCR](https://github.com/open-mmlab/mmocr): OpenMMLab text detection, recognition, and understanding toolbox.
- [MMPose](https://github.com/open-mmlab/mmpose): OpenMMLab pose estimation toolbox and benchmark.
- [MMHuman3D](https://github.com/open-mmlab/mmhuman3d): OpenMMLab 3D human parametric model toolbox and benchmark.
- [MMSelfSup](https://github.com/open-mmlab/mmselfsup): OpenMMLab self-supervised learning toolbox and benchmark.
- [MMRazor](https://github.com/open-mmlab/mmrazor): OpenMMLab model compression toolbox and benchmark.
- [MMFewShot](https://github.com/open-mmlab/mmfewshot): OpenMMLab fewshot learning toolbox and benchmark.
- [MMAction2](https://github.com/open-mmlab/mmaction2): OpenMMLab's next-generation action understanding toolbox and benchmark.
- [MMTracking](https://github.com/open-mmlab/mmtracking): OpenMMLab video perception toolbox and benchmark.
- [MMPose](https://github.com/open-mmlab/mmpose): OpenMMLab pose estimation toolbox and benchmark.
- [MMFlow](https://github.com/open-mmlab/mmflow): OpenMMLab optical flow toolbox and benchmark.
- [MMEditing](https://github.com/open-mmlab/mmediting): OpenMMLab image and video editing toolbox.
- [MMOCR](https://github.com/open-mmlab/mmocr): A Comprehensive Toolbox for Text Detection, Recognition and Understanding.
- [MMGeneration](https://github.com/open-mmlab/mmgeneration): OpenMMLab image and video generative models toolbox.
- [MMFlow](https://github.com/open-mmlab/mmflow): OpenMMLab optical flow toolbox and benchmark.
- [MMFewShot](https://github.com/open-mmlab/mmfewshot): OpenMMLab FewShot Learning Toolbox and Benchmark.
- [MMHuman3D](https://github.com/open-mmlab/mmhuman3d): OpenMMLab Human Pose and Shape Estimation Toolbox and Benchmark.
- [MMSelfSup](https://github.com/open-mmlab/mmselfsup): OpenMMLab self-supervised learning Toolbox and Benchmark.
- [MMRazor](https://github.com/open-mmlab/mmrazor): OpenMMLab Model Compression Toolbox and Benchmark.
- [MMDeploy](https://github.com/open-mmlab/mmdeploy): OpenMMLab Model Deployment Framework.
- [MMDeploy](https://github.com/open-mmlab/mmdeploy): OpenMMLab model deployment framework.
16 changes: 9 additions & 7 deletions README_zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ MMDeploy 是一个开源深度学习模型部署工具箱,它是 [OpenMMLab](h
- [x] MMSegmentation
- [x] MMEditing
- [x] MMOCR
- [x] MMPose

- **支持多种推理后端**

Expand Down Expand Up @@ -114,18 +115,19 @@ MMDeploy 是一个开源深度学习模型部署工具箱,它是 [OpenMMLab](h
- [MMClassification](https://github.com/open-mmlab/mmclassification): OpenMMLab 图像分类工具箱
- [MMDetection](https://github.com/open-mmlab/mmdetection): OpenMMLab 目标检测工具箱
- [MMDetection3D](https://github.com/open-mmlab/mmdetection3d): OpenMMLab 新一代通用 3D 目标检测平台
- [MMRotate](https://github.com/open-mmlab/mmrotate): OpenMMLab 旋转框检测工具箱与测试基准
- [MMSegmentation](https://github.com/open-mmlab/mmsegmentation): OpenMMLab 语义分割工具箱
- [MMAction2](https://github.com/open-mmlab/mmaction2): OpenMMLab 新一代视频理解工具箱
- [MMTracking](https://github.com/open-mmlab/mmtracking): OpenMMLab 一体化视频目标感知平台
- [MMPose](https://github.com/open-mmlab/mmpose): OpenMMLab 姿态估计工具箱
- [MMEditing](https://github.com/open-mmlab/mmediting): OpenMMLab 图像视频编辑工具箱
- [MMOCR](https://github.com/open-mmlab/mmocr): OpenMMLab 全流程文字检测识别理解工具包
- [MMGeneration](https://github.com/open-mmlab/mmgeneration): OpenMMLab 图片视频生成模型工具箱
- [MMFlow](https://github.com/open-mmlab/mmflow): OpenMMLab 光流估计工具箱与测试基准
- [MMFewShot](https://github.com/open-mmlab/mmfewshot): OpenMMLab 少样本学习工具箱与测试基准
- [MMPose](https://github.com/open-mmlab/mmpose): OpenMMLab 姿态估计工具箱
- [MMHuman3D](https://github.com/open-mmlab/mmhuman3d): OpenMMLab 人体参数化模型工具箱与测试基准
- [MMSelfSup](https://github.com/open-mmlab/mmselfsup): OpenMMLab 自监督学习工具箱与测试基准
- [MMRazor](https://github.com/open-mmlab/mmrazor): OpenMMLab 模型压缩工具箱与测试基准
- [MMFewShot](https://github.com/open-mmlab/mmfewshot): OpenMMLab 少样本学习工具箱与测试基准
- [MMAction2](https://github.com/open-mmlab/mmaction2): OpenMMLab 新一代视频理解工具箱
- [MMTracking](https://github.com/open-mmlab/mmtracking): OpenMMLab 一体化视频目标感知平台
- [MMFlow](https://github.com/open-mmlab/mmflow): OpenMMLab 光流估计工具箱与测试基准
- [MMEditing](https://github.com/open-mmlab/mmediting): OpenMMLab 图像视频编辑工具箱
- [MMGeneration](https://github.com/open-mmlab/mmgeneration): OpenMMLab 图片视频生成模型工具箱
- [MMDeploy](https://github.com/open-mmlab/mmdeploy): OpenMMLab 模型部署框架

## 欢迎加入 OpenMMLab 社区
Expand Down
151 changes: 151 additions & 0 deletions cmake/MMDeploy.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# Copyright (c) OpenMMLab. All rights reserved.

function (mmdeploy_export NAME)
set(_LIB_DIR lib)
if (MSVC)
set(_LIB_DIR bin)
endif ()
install(TARGETS ${NAME}
EXPORT MMDeployTargets
ARCHIVE DESTINATION lib
LIBRARY DESTINATION ${_LIB_DIR}
RUNTIME DESTINATION bin)
endfunction ()


function (mmdeploy_add_library NAME)
cmake_parse_arguments(_MMDEPLOY "EXCLUDE" "" "" ${ARGN})
add_library(${NAME} ${_MMDEPLOY_UNPARSED_ARGUMENTS})
target_compile_definitions(${NAME} PRIVATE -DMMDEPLOY_API_EXPORTS=1)
get_target_property(_TYPE ${NAME} TYPE)
if (_TYPE STREQUAL STATIC_LIBRARY)
set_target_properties(${NAME} PROPERTIES POSITION_INDEPENDENT_CODE 1)
elseif (_TYPE STREQUAL SHARED_LIBRARY)
else ()
message(FATAL_ERROR "unsupported type: ${_TYPE}")
endif ()
if (NOT _MMDEPLOY_EXCLUDE)
target_link_libraries(MMDeployLibs INTERFACE ${NAME})
mmdeploy_export(${NAME})
endif ()
endfunction ()


function (mmdeploy_add_module NAME)
# EXCLUDE: exclude from registering & exporting as SDK module
# LIBRARY: the module is also a library (add_libray with SHARED instead of MODULE)
cmake_parse_arguments(_MMDEPLOY "EXCLUDE;LIBRARY" "" "" ${ARGN})
# search for add_library keywords
cmake_parse_arguments(_KW "STATIC;SHARED;MODULE" "" "" ${_MMDEPLOY_UNPARSED_ARGUMENTS})

set(_MAYBE_MODULE)
# no library type specified
if (NOT (_KW_STATIC OR _KW_SHARED OR _KW_MODULE))
# shared but not marked as a library, build module library so that no .lib dependency
# will be generated for MSVC
if (MSVC AND BUILD_SHARED_LIBS AND NOT _MMDEPLOY_LIBRARY)
set(_MAYBE_MODULE MODULE)
endif ()
endif ()

add_library(${NAME} ${_MAYBE_MODULE} ${_MMDEPLOY_UNPARSED_ARGUMENTS})

# automatically link mmdeploy::core if exists
if (TARGET mmdeploy::core)
target_link_libraries(${NAME} PRIVATE mmdeploy::core)
endif ()

# export public symbols when marked as a library
if (_MMDEPLOY_LIBRARY)
target_compile_definitions(${NAME} PRIVATE -DMMDEPLOY_API_EXPORTS=1)
endif ()

get_target_property(_TYPE ${NAME} TYPE)
if (_TYPE STREQUAL STATIC_LIBRARY)
set_target_properties(${NAME} PROPERTIES POSITION_INDEPENDENT_CODE 1)
if (MSVC)
target_link_options(${NAME} INTERFACE "/WHOLEARCHIVE:${NAME}")
endif ()
# register static modules
if (NOT _MMDEPLOY_EXCLUDE)
target_link_libraries(MMDeployStaticModules INTERFACE ${NAME})
endif ()
elseif (_TYPE STREQUAL SHARED_LIBRARY OR _TYPE STREQUAL MODULE_LIBRARY)
# register dynamic modules
if (NOT _MMDEPLOY_EXCLUDE)
target_link_libraries(MMDeployDynamicModules INTERFACE ${NAME})
endif ()
else ()
message(FATAL_ERROR "unsupported type: ${_TYPE}")
endif ()
if (NOT _MMDEPLOY_EXCLUDE)
mmdeploy_export(${NAME})
endif ()
endfunction ()


function (_mmdeploy_flatten_modules RETVAL)
set(_RETVAL)
foreach (ARG IN LISTS ARGN)
get_target_property(TYPE ${ARG} TYPE)
if (TYPE STREQUAL "INTERFACE_LIBRARY")
get_target_property(LIBS ${ARG} INTERFACE_LINK_LIBRARIES)
if (LIBS)
# pattern for 3.17+
list(FILTER LIBS EXCLUDE REGEX "^::@")
# pattern for 3.13-3.16
list(TRANSFORM LIBS REPLACE "(.+)::@.*" "\\1")
list(APPEND _RETVAL ${LIBS})
endif ()
else ()
list(APPEND _RETVAL ${ARG})
endif ()
endforeach ()
set(${RETVAL} ${_RETVAL} PARENT_SCOPE)
endfunction ()


function (mmdeploy_load_static NAME)
if (MSVC)
target_link_libraries(${NAME} PRIVATE ${ARGN})
else ()
_mmdeploy_flatten_modules(_MODULE_LIST ${ARGN})
target_link_libraries(${NAME} PRIVATE
-Wl,--whole-archive
${_MODULE_LIST}
-Wl,--no-whole-archive)
endif ()
endfunction ()

function (mmdeploy_load_dynamic NAME)
_mmdeploy_flatten_modules(_MODULE_LIST ${ARGN})
if (MSVC)
if (NOT _MODULE_LIST)
return ()
endif ()
# MSVC has nothing like "-Wl,--no-as-needed ... -Wl,--as-needed", as a
# workaround we build a static module which loads the dynamic modules
set(_MODULE_STR ${_MODULE_LIST})
list(TRANSFORM _MODULE_STR REPLACE "(.+)" "\"\\1\"")
string(JOIN ",\n " _MODULE_STR ${_MODULE_STR})
set(_MMDEPLOY_DYNAMIC_MODULES ${_MODULE_STR})

set(_LOADER_NAME ${NAME}_loader)

add_dependencies(${NAME} ${_MODULE_LIST})

set(_LOADER_PATH ${CMAKE_BINARY_DIR}/${_LOADER_NAME}.cpp)
# ! CMAKE_CURRENT_FUNCTION_LIST_DIR requires cmake 3.17+
configure_file(
${CMAKE_CURRENT_FUNCTION_LIST_DIR}/loader.cpp.in
${_LOADER_PATH})

mmdeploy_add_module(${_LOADER_NAME} STATIC EXCLUDE ${_LOADER_PATH})
mmdeploy_load_static(${NAME} ${_LOADER_NAME})
else ()
target_link_libraries(${NAME} PRIVATE
-Wl,--no-as-needed
${_MODULE_LIST}
-Wl,--as-needed)
endif ()
endfunction ()
17 changes: 10 additions & 7 deletions cmake/MMDeployConfig.cmake.in
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,26 @@

cmake_minimum_required(VERSION 3.14)

include ("${CMAKE_CURRENT_LIST_DIR}/MMDeployTargets.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/MMDeployTargets.cmake")

set(MMDEPLOY_CODEBASES @MMDEPLOY_CODEBASES@)
set(MMDEPLOY_TARGET_DEVICES @MMDEPLOY_TARGET_DEVICES@)
set(MMDEPLOY_TARGET_BACKENDS @MMDEPLOY_TARGET_BACKENDS@)
set(MMDEPLOY_BUILD_TYPE @CMAKE_BUILD_TYPE@)
set(MMDEPLOY_STATIC_MODULES @MMDEPLOY_STATIC_MODULES@)
set(MMDEPLOY_DYNAMIC_MODULES @MMDEPLOY_DYNAMIC_MODULES@)
set(MMDEPLOY_BUILD_SHARED @BUILD_SHARED_LIBS@)
set(MMDEPLOY_LIBS @MMDEPLOY_LIBS@)

if (NOT MMDEPLOY_BUILD_SHARED)
if ("cuda" IN_LIST MMDEPLOY_TARGET_DEVICES)
set(CMAKE_CUDA_RUNTIME_LIBRARY Shared)
enable_language(CUDA)
find_package(pplcv REQUIRED)
endif ()
endif ()

set(MMDeploy_LIBS ${MMDEPLOY_LIBS}
-Wl,--no-as-needed ${MMDEPLOY_DYNAMIC_MODULES} -Wl,--as-needed
-Wl,--whole-archive ${MMDEPLOY_STATIC_MODULES} -Wl,--no-whole-archive)
find_package(spdlog REQUIRED)
find_package(OpenCV REQUIRED)

set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)

include("${CMAKE_CURRENT_LIST_DIR}/MMDeploy.cmake")
Loading

0 comments on commit 0f12f50

Please sign in to comment.